浏览器内置对象、浏览器事件模型、浏览器请求的相关内容请求理解

·  阅读 285

一.什么是浏览器对象模型

BOM :Browser Object Model(浏览器对象模型),浏览器模型提供了独立于内容的、可以与浏览器窗口进行滑动的对象结构,就是浏览器提供的 API 其主要对象有:

  1. window 对象——BOM 的核心,是 js 访问浏览器的接口,也是 ES 规定的 Global 对象
  2. location 对象:提供当前窗口中的加载的文档有关的信息和一些导航功能。既是 window 对象属 性,也是 document 的对象属性
  3. navigation 对象:获取浏览器的系统信息
  4. screen 对象:用来表示浏览器窗口外部的显示器的信息等
  5. history 对象:保存用户上网的历史信息

1.Window 对象

windows对象是对整个浏览器对象模型对核心,其是接口又是全局对象对角色

  • alert():系统警告对话框,可以接收字符串参数显示

window.alert(message); // message 是要显示在对话框中的文本字符串
比如:
window.alert(‘你好,世界真美好’);
复制代码

截屏2021-04-20 23.10.27.png

  • confirm(): 系统确认框对话框,可以提供确认和取消2种事件
result = windows.confirm(message);
// message 是要在对话框中显示的可选字符串
// result 是一个布尔值,表示可以选中还是可以取消
window.confirm('你在看什么');
复制代码

截屏2021-04-20 23.17.32.png

  • prompt(): 提示对话框,可以用用户展示确认,取消事件外,还可以提供文本域
res = window.prompt(text, value);
// res是用来存住输入的值,或者是为空值null
// text 用来存输入文字的字符串,如果没有任何提示可以不写
// value 是文本输入框中的值,也可以忽略

let text = window.prompt(‘很高兴认识你’);
复制代码

截屏2021-04-20 23.28.06.png

  • open():可以创建一个跨浏览器对象,可以导航至特定对url,也可以打开新对浏览器窗口,
window.open(要加载的url, 窗口目标, 一个特定字符串, 一个新页面是否取代浏览器历史记录中当前加载页面的布尔值)

let strWindowFeatures = `
    menubar=yes,
    location=yes,
    resizable=yes,
    scrollbars=yes,
    status=yes
`;

function openRequestedPopup() {
    windowObjectReference =
    window.open(
        "http://www.cnn.com/",
        "CNN_WindowName",
        strWindowFeatures
    );
}
openRequestedPopup();
// 就会创建一个新的窗口,跳到cnn.com
复制代码
  • onerror(): 事件处理的程序,当没有捕获都异常传播到调用栈上的时候就会去调用它,并且会把错误的信息输出到浏览器的控制台上面
windows.onerror = function(message,source,lineno, colno,error) {...};

message:错误信息(字符串)
source:发生错误的脚本URL(字符串)
lineno:发生错误的行号(数字)
colno:发生错误的列号(数字)
error:Error对象(对象)

// 例子:
window.onerror = function (msg, url, lineNo, columnNo, error) {
    var string = msg.toLowerCase();
    var substring = "script error";
    if (string.indexOf(substring) > -1){
        alert('Script Error: See Browser Console for Detail');
    } else {
        var message = [
            'Message: ' + msg,
            'URL: ' + url,
            'Line: ' + lineNo,
            'Column: ' + columnNo,
            'Error object: ' + JSON.stringify(error)
        ].join(' - ');

        alert(message);
    }

    return false;
};
复制代码
  • setTimeout():超时的时候调用,是指过了多少时间以后在执行代码或者方法

window.setTimeout(()=>{ console.log('我执行了') }, 1000(毫秒))

  • setInterval():间隔调用 每隔多少时间执行一次代码或者方法

window.setInterval(()=>{ console.log('我执行了') }, 1000(毫秒))

// 使用 setInterval 来执行倒计时
let num = 10;
let interval = setInterval(()=> {
    num --;
    console.log(num);
    if(num === 0) {
        clearInterval(interval);
        console.log('done');
    }
}, 1000);

// 使用 setTimeout 来执行倒计时
let num = 10;
let timeout = setTimeout(() => {
    num --;
    console.log(num);
    if(num > 0) {
        setTimeout(timeout, 1000);
    }else {
        console.log('done');
    }

},1000);
setTimeout(timeout, 1000);
复制代码

image.png

浏览器窗口方法

截屏2021-04-21 21.22.58.png

// 获取浏览器窗口左边和上边的位置
let leftPos = (type window.screenLeft === 'number') ? window.screenLeft : window.screenX;
let topPos = (type window.screenTop === 'number') ? window.screenTop : window.screenY;

window.moveBy(x, y);
// x 表示窗口水平移动方向的像素值
// y 表示 表示窗口在垂直方向移动的像素值
function test() {
    moveBy(10, -10);
}

window.moveTo(x,y);
// x 是移动到的位置的横坐标
// y 是移动到的位置的纵坐标
function test() {
    // 把窗口移动到左上角
    moveTo(10, -10);
}

复制代码

浏览器窗口大小方法

截屏2021-04-21 22.02.14.png

// 获取可视口的宽度和高度
let inFrameWith = window.innerWith();
let inFrameHight = window.innerHeight();

// 获取整个浏览器窗口的高度和宽度
let inWindowWith = window.outerHeight();
let inWindowHeight = window.outerWith();

// 改变窗口的大小 (x,y)
// x为窗口水平方向变化像素值
// y为窗口垂直方向变化的像素值
window.resizeBy(-200,200);

// 动态调整窗口大小
// x是一个整数,表示新的outerWith();(包括滚动条和窗口边框)
// 是一个整数,表示新的outerHeight();(包括滚动条和窗口边框)
window.resizeTo(x,y);

function queater() {
    window.resizeTo(
        window.screen.x / 2;
        window.screen.y / 2;
    );

}


复制代码

2.location 对象

  • 提供当前窗口中加载文档有关信息和一些导航功能,是window对象属性,也有document的对象属性

loaction 对象的主要属性;

截屏2021-04-21 22.26.17.png

//location.hash
<a id="test" href="/en/docs/location.href#hello">hello</a>
// js部分
let test = document.getElementById('test');
console.log(test.hash); //返回hello


// location.host
let test = document.createElement(‘test’);
test.href = ‘http://developer.mozilla.org:443/location.host’;
test.host = ‘developer.mozlla.org:443// host没有包含端口号,因为443是https协议的默认端口号
 
 
//location.hostname
<a id="test" href="http://developer.mozilla.org/location.hostname">hello</a>
let test = document.createElement(‘test’);
let result = test.hostname; // 结果为developer.mozilla.org

// location.href
<a id="test" href="http://developer.mozilla.org/href">hello</a>
let test = document.createElement(‘test’);
let result = test.hostname; // 结果为http://developer.mozilla.org/href


// location.pathname
<a id="test" href="/en/docs/location.pathname">hello</a>
let test = document.createElement(‘test’);
let result = test.hostname; // 结果为/en/docs/location.pathname


// location.port
<a id="test" href="http://developer.mozilla.org:443/href">hello</a>
let test = document.createElement(‘test’);
test.href = ‘https://developer.mozilla.org:443/location.port’;
let result = test.prot; // 就是443是httpsl议的默认端口号


// location.protocol
<a id="test" href="https://developer.mozilla.org/href">hello</a>
let test = document.createElement(‘test’);
let result = test.protocol; // 结果为https


// location.search
<a id="test" href="https://developer.mozilla.org/href?q=123">hello</a>
let test = document.createElement(‘test’);
let result = test.search; // 结果为?q=123

let params = new URLSearchParams('result');
let q = parseInt(params.get(q)); // 结果就是123
复制代码
  • location的应用场景

1.解析 url查询字符串的参数,并且返回一个对象;

  • 通过location的search的属性来获取当前url中传递的参数,如果url中查询到含有问号的字符串,就将其截取掉,然后遍历里面的字符串并且以等号为断点,使用decodeURIComponent()方法来解析具体的参数值,并将其放在对象的容器中返回;

2.入新的页面.或者说是刷新页面使用,包含了三个方法

  • assign: location.assign(‘www.baidu.com’) 就可以立即打开新的url,并且会在浏览器的历史记录中生成一条新的记录,
  • replace: location.replace(‘www.baidu.com’) 只接受一个url参数,通过跳转url界面,不会在浏览器中生成历史记录,但是会替换掉当前页面
  • reload: location.reload()其作用就是重新加载当前显示的页面,当不传递参数的时候,如果页面上次请求以来没有改变过,页面就会从浏览器中重新加载一次,如果传递true,则会强制从服务器重新加载一起,相当于window系统的ctrl+F5

3.history 对象

  • history 对象保存着用户上网的历史记录,从窗口被打开的那一刻算起,history 对象是用窗口 的浏览历史用文档和文档状态列表的形式表示.

截屏2021-04-22 22.18.50.png

window.history.back();     // 等同于点击浏览器的回退按钮
window.history.go(-1);     //等同于history.back();


// window.history.forward();代表前进一页
<button id='test'>hello,word</button>
window.onload = function(e) {
  document.getElementById('test').addEventListener('click', e => {
    window.history.forward();
  })
}

let framesCount = window.length; // framesCount就是该窗口中框架的数量

复制代码

二.浏览器事件模型

1.事件订阅

浏览器的交互是通过浏览器的内置事件响应实现的,常见的浏览器事件有文档载入(load),元素点击(click)等,订阅浏览器事件可以通过如下方式实现。

  • HTML属性的事件句柄,如:onclick
  • DOM属性的事件句柄,在js中为dom添加onclick方法
  • 事件句柄注册:IE8+(window的两个方法addEventListener和removeEventListener)和IE8及以下(attachEvent与detachEvent)

2.事件传播

事件传播分为3个阶段:捕获阶段,目标阶段,冒泡阶段

// html部分
<div id="parent" class="flex-center">
        parent
        <p id="child" class="flex-center">
            child
            <span id='son' class="flex-center">
                son
            </span>
        </p>
</div>
复制代码
// css 部分
#parent {
        background-color: bisque;
        width: 700px;
        height: 700px;

    }

    #child {
        background-color: chocolate;
        width: 500px;
        height: 500px;
    }

    #son {
        background-color: crimson;
        width: 300px;
        height: 300px;
    }

    .flex-center {
        display: flex;
        justify-content: center;
        align-items: center;
        font-size: 20px;
    }
复制代码
// js部分
const parent = document.getElementById("parent");
const child = document.getElementById("child");
const son = document.getElementById("son");

window.addEventListener("click", function (e) {
    // e.target.nodeName 指当前点击的元素, e.currentTarget.nodeName绑定监听事件的元素
    console.log("window 捕获", e.target.nodeName, e.currentTarget.nodeName);
}, true);

parent.addEventListener("click", function (e) {
    // e.stopPropagation();

    // e.target.nodeName 指当前点击的元素, e.currentTarget.nodeName绑定监听事件的元素
    console.log("parent 捕获", e.target.nodeName, e.currentTarget.nodeName);
}, true);

child.addEventListener("click", function (e) {
    console.log("child 捕获", e.target.nodeName, e.currentTarget.nodeName);
}, true);

son.addEventListener("click", function (e) {
    console.log("son 捕获", e.target.nodeName, e.currentTarget.nodeName);
}, true);

window.addEventListener("click", function (e) {
    // e.target.nodeName 指当前点击的元素, e.currentTarget.nodeName绑定监听事件的元素
    console.log("window 冒泡", e.target.nodeName, e.currentTarget.nodeName);
}, false);

parent.addEventListener("click", function (e) {
    console.log("parent 冒泡", e.target.nodeName, e.currentTarget.nodeName);
}, false);

child.addEventListener("click", function (e) {
    console.log("child 冒泡", e.target.nodeName, e.currentTarget.nodeName);
}, false);

son.addEventListener("click", function (e) {
    console.log("son 冒泡", e.target.nodeName, e.currentTarget.nodeName);
}, false);
复制代码

看下页面的demo效果:

截屏2021-04-22 23.12.53.png

总结:

  • 捕获阶段:首先window会获捕获到事件,之后document、documentElement、body会捕获到,再之后就是在body中DOM元素一层一层的捕获到事件,有div、child。
  • 目标阶段:真正点击的元素son的事件发生了两次,因为在上面的JavaScript代码中,son既在捕获阶段绑定了事件,又在冒泡阶段绑定了事件,所以发生了两次。但是这里有一点是需要注意,在目标阶段并不一定先发生在捕获阶段所绑定的事件,而是先绑定的事件发生
  • 冒泡阶段:会和捕获阶段相反的步骤将事件一步一步的冒泡到window

3.阻止事件传播

  • e.stopPropagation(): 说到事件,我们经常听到的是阻止冒泡,实际上呢这个方法不仅仅只能阻止事件的冒泡,还能阻止事件捕获阶段的传播

改下上面的js代码:

const parent = document.getElementById("parent");
const child = document.getElementById("child");
const son = document.getElementById("son");
const flag = ture;

window.addEventListener("click", function (e) {
    // e.target.nodeName 指当前点击的元素, e.currentTarget.nodeName绑定监听事件的元素
    console.log("window 捕获", e.target.nodeName, e.currentTarget.nodeName);
}, true);

parent.addEventListener("click", function (e) {
    if (flag) {
        e.stopPropagation();
        window.alert('你点不到我');
        return;
    }

    // e.target.nodeName 指当前点击的元素, e.currentTarget.nodeName绑定监听事件的元素
    console.log("parent 捕获", e.target.nodeName, e.currentTarget.nodeName);
}, true);

child.addEventListener("click", function (e) {
    console.log("child 捕获", e.target.nodeName, e.currentTarget.nodeName);
}, true);

son.addEventListener("click", function (e) {
    console.log("son 捕获", e.target.nodeName, e.currentTarget.nodeName);
}, true);

window.addEventListener("click", function (e) {
    // e.target.nodeName 指当前点击的元素, e.currentTarget.nodeName绑定监听事件的元素
    console.log("window 冒泡", e.target.nodeName, e.currentTarget.nodeName);
}, false);

parent.addEventListener("click", function (e) {
    console.log("parent 冒泡", e.target.nodeName, e.currentTarget.nodeName);
}, false);

child.addEventListener("click", function (e) {
    console.log("child 冒泡", e.target.nodeName, e.currentTarget.nodeName);
}, false);

son.addEventListener("click", function (e) {
    console.log("son 冒泡", e.target.nodeName, e.currentTarget.nodeName);
}, false);

复制代码

看下页面效果:

截屏2021-04-22 23.22.27.png

4.阻止事件默认行为

  • e.preventDefault():可以阻止事件的默认行为发生,默认行为是指:点击a标签就转跳到其他页面、拖拽一个图片到浏览器会自动打开、点击表单的提交按钮会提交表单等等,因为有的时候我们并不希望发生这些事情,所以需要阻止默认行为

接着改下上面的代码:

// html部分 
<div id="parent" class="flex-center">
        parent
        <p id="child" class="flex-center">
            child
            <span id='son' class="flex-center">
                son
                <a href="https://www.baidu.com" id="a-baidu">点我啊</a>
            </span>
        </p>
    </div>
复制代码
// js部分
const parent = document.getElementById("parent");
const child = document.getElementById("child");
const son = document.getElementById("son");
const baidu = document.getElementById("a-baidu");

baidu.addEventListener('click', function (e) {
    e.preventDefault();
})

window.addEventListener("click", function (e) {
    // e.target.nodeName 指当前点击的元素, e.currentTarget.nodeName绑定监听事件的元素
    console.log("window 捕获", e.target.nodeName, e.currentTarget.nodeName);
}, true);

parent.addEventListener("click", function (e) {
    // e.stopPropagation();

    // e.target.nodeName 指当前点击的元素, e.currentTarget.nodeName绑定监听事件的元素
    console.log("parent 捕获", e.target.nodeName, e.currentTarget.nodeName);
}, true);

child.addEventListener("click", function (e) {
    console.log("child 捕获", e.target.nodeName, e.currentTarget.nodeName);
}, true);

son.addEventListener("click", function (e) {
    console.log("son 捕获", e.target.nodeName, e.currentTarget.nodeName);
}, true);

window.addEventListener("click", function (e) {
    // e.target.nodeName 指当前点击的元素, e.currentTarget.nodeName绑定监听事件的元素
    console.log("window 冒泡", e.target.nodeName, e.currentTarget.nodeName);
}, false);

parent.addEventListener("click", function (e) {
    console.log("parent 冒泡", e.target.nodeName, e.currentTarget.nodeName);
}, false);

child.addEventListener("click", function (e) {
    console.log("child 冒泡", e.target.nodeName, e.currentTarget.nodeName);
}, false);

son.addEventListener("click", function (e) {
    console.log("son 冒泡", e.target.nodeName, e.currentTarget.nodeName);
}, false);
复制代码

看下代码效果:

截屏2021-04-22 23.27.51.png

5.兼容性,封装一个多个浏览器兼容的绑定事件函数


class conmonEvent {
    construtor(e) {
        this.element = e;
    }
    
    addEvent(type, handler) {
        if(this.element.addEventListener) {
            this.element.addEventListener(type, handler, false);
        } else if (this.element.attachEvent) {
            this.element.attachEvent('on' +type, function () {
                handler.call(element);
            });
        } else {
            this.element['on'+ type] = handler;
        
        }
    }
    
    removeEvent(type, handler) {
        if(this.element.removeEventListener) {
            this.element.removeEventListener(type, handler, false);
        } else if (this.element.detachEvent) {
            this.element.attachEvent('on' +type, function () {
                handler.call(element);
            });
        } else {
            this.element['on'+ type] = null;
        
        }
    
    }

}

// 阻止事件 (主要是事件冒泡,因为IE不支持事件捕获)
function stopPropagation(ev) {
    if (ev.stopPropagation) {
        ev.stopPropagation(); // 标准w3c
    } else {
        ev.cancelBubble = true; // IE
    }
}
// 取消事件的默认行为
function preventDefault(event) {
    if (event.preventDefault) {
        event.preventDefault(); // 标准w3c
    } else {
        event.returnValue = false; // IE
    }
}
复制代码

三.浏览器请求相关内容

1.XMLHTTPRequest

  • XMLHttpRequest 对象提供了对 HTTP 协议的完全的访问,包括做出 POST 和 HEAD 请求以及普通的 GET 请求的能力。XMLHttpRequest 可以同步或异步地返回 Web 服务器的响应,并且能够以文本或者一个 DOM 文档的形式返回内容。

截屏2021-04-22 23.53.30.png

let xhr = new XMLHttpRequest();
xhr.open('GET', 'http://domain/service');

// request state change event
xhr.onreadystatechange = function () {
    // request completed?
    if (xhr.readyState !== 4) return;

    if (xhr.status === 200) {
        // request successful - show response
        console.log(xhr.responseText);
    } else {
        // request error
        console.log('HTTP error', xhr.status, xhr.statusText);
    }
};

xhr.timeout = 3000; // 3 seconds
xhr.ontimeout = () => console.log('timeout', xhr.responseURL);

// progress事件可以报告长时间运行的文件上传
xhr.upload.onprogress = p => {
    console.log(Math.round((p.loaded / p.total) * 100) + '%');
}

// start request
xhr.send();

复制代码

2.fetch

  • fetch是一种HTTP数据请求的方式,是XMLHttpRequest的一种替代方案。fetch不是ajax的进一步封装,而是原生js。Fetch函数就是原生js,没有使用XMLHttpRequest对象。

1.默认不带cookie

fetch(‘https://domain/service’, {
    method:'GET',
    credentials: 'same-oringin'
})
复制代码

2.错误不会reject

// http错误 ,比如404 500 ,不会导致fetch返回带pormise标记为reject, catch并不会被触发
// 想要精确的判断fetch是否成功,需要包含promise resolveed的情况,判断response.ok 是不是为true

fetch(‘https://domain/service’, {
    method:'GET',
    }).then((response) => {
        // 404 500
        if (response.ok) {
           return response.json();
        }else {
            throw new Error('fetch error');
        }
        
    }).then(json => {
        console.log(json);
    
    }).catch(error => {
        console.log('error');
    });
})

复制代码

3.不支持设置超时

function fetchTimeout(url, init, timeout = 3000) {
    return new Promise((resolve, reject) => {
        fetch(url, init)
            .then(resolve)
            .catch(reject);
        setTimeout(reject, timeout);
    })
}
复制代码

4.终止fetch

const controller = new AbortController();

fetch(
        'http://domain/service', {
            method: 'GET',
            signal: controller.signal
        })
    .then(response => response.json())
    .then(json => console.log(json))
    .catch(error => console.error('Error:', error));

controller.abort();
复制代码

5.封装一个ajax请求

type AjaxType = 'GET' || 'POST' || 'PUT' || 'DELETE';
interface setOption {
    url: string;
    type?: AjaxType;
    data: any;
    timeout?: number
}

function formatUrl(json) {
    let dataArr = [];
    json.t = Math.random();
    for (let key in json) {
        dataArr.push(`${key}=${encodeURIComponent(json[key])}`)
    }
    return dataArr.join('&');
}

export function ajax(options: IOptions) {
    return new Promise((resolve, reject) => {
        if (!options.url) return;

        options.type = options.type || 'GET';
        options.data = options.data || {};
        options.timeout = options.timeout || 10000;
    
        let dataToUrlstr = formatUrl(options.data);
        let timer;
    
        // 1.创建
        let xhr;
        if ((window as any).XMLHttpRequest) {
            xhr = new XMLHttpRequest();
        } else {
            xhr = new ActiveXObject('Microsoft.XMLHTTP');
        }
    
        if (options.type.toUpperCase() === 'GET') {
            // 2.连接
            xhr.open('get', `${options.url}?${dataToUrlstr}`, true);
            // 3.发送
            xhr.send();
        } else if (options.type.toUpperCase() === 'POST') {
            // 2.连接
            xhr.open('post', options.url, true);
            xhr.setRequestHeader('ContentType', 'application/x-www-form-urlencoded');
            // 3.发送
            xhr.send(options.data);
        }
    
        // 4.接收
        xhr.onreadystatechange = () => {
            if (xhr.readyState === 4) {
                clearTimeout(timer);
                if (xhr.status >= 200 && xhr.status < 300 || xhr.status === 304) {
                    resolve(xhr.responseText);
                } else {
                    reject(xhr.status);
                }
            }
        }
    
        if (options.timeout) {
            timer = setTimeout(() => {
                xhr.abort();
                reject('超时');
            }, options.timeout)
        }

        xhr.timeout = options.timeout;
        xhr.ontimeout = () => {
           reject('超时');
         }
    });
}

复制代码

参考文章

浏览器内置对象/事件

2.6万字JS干货分享,带你领略前端魅力!

浏览器事件系统

分类:
前端
标签: