学习笔记:浏览器相关

148 阅读5分钟

Based by 路白

1.浏览器内置对象详解

常见浏览器 JS 对象常见 API 及用法

segmentfault.com/a/119000001…

什么是浏览器对象模型

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

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

Window 对象

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

alert() confirm() prompt() open() onerror() setTimeout() setInterval()

  • 窗口位置

screenLeft screenTop screenX screenY moveBy(x,y) moveTo(x,y)

  • 窗口大小

innerWidth innerHeight

outerWidth outerHeight

resizeTo(width, height) resizeBy(width, height)

  • 定时器

setTimeout setInterval

Location 对象

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

location 对象的主要属性: hash host hostname href pathname port protocol search

location 的应用场景: 讲课时补充

Navigation 对象

navigation 接口表示用户代理的状态和标识,允许脚本查询它和注册自己进行一些活动

History 对象

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

go() back() forword() length

2.浏览器事件模型详解

详解浏览器事件捕获,冒泡

浏览器事件模型中的过程主要分为三个阶段:捕获阶段、目标阶段、冒泡阶段。 这里先看一下这张经典的图

第三个参数

这里要注意addEventListener的第三个参数, 如果为true,就是代表在捕获阶段执行。如果为false,就是在冒泡阶段进行 空口这么说可能不好理解,咱们来看一下代码。

阻止事件传播

  • e.stopPropagation()

大家经常听到的可能是阻止冒泡,实际上这个方法不只能阻止冒泡,还能阻止捕获阶段的传播。

  • stopImmediatePropagation() 如果有多个相同类型事件的事件监听函数绑定到同一个元素,当该类型的事件触发时,它们会按照被添加的顺序执行。如果其中某个监听函数执行了 event.stopImmediatePropagation() 方法,则当前元素剩下的监听函数将不会被执行。

看一下代码

阻止默认行为

e.preventDefault()

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

看一下代码

兼容性

attachEvent——兼容:IE7、IE8; 不支持第三个参数来控制在哪个阶段发生,默认是绑定在冒泡阶段 addEventListener——兼容:firefox、chrome、IE、safari、opera;

绑定事件的运用,以及封装一个多浏览器兼容的绑定事件函数

大家常见的一个面试题可能是ul + li,点击每个li alert对应的索引,这里就给大家来写一下看看

  • 先来给每个li绑定事件

  • 再来写一个事件委托的方式

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

3.浏览器请求相关内容详解

ajax 及 fetch API 详解

  1. XMLHTTPRequest

  2. fetch

  • 默认不带cookie
  • 错误不会reject
  • 不支持超时设置
  • 需要借用AbortController中止fetch

常见的浏览器请求/响应头/错误码解析

request header

:method: GET :path: :scheme: https accept: application/json, text/plain, / accept-encoding: gzip, deflate, br cache-control: no-cache cookie: deviceId=c12; origin: referer: user-agent: Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1

response header

access-control-allow-credentials: true access-control-allow-origin: content-encoding: gzip content-type: application/json;charset=UTF-8 date: Thu, 06 Aug 2020 08:15:05 GMT set-cookie: sess=QvrAQ0Cq+EcDQQPTer2X; status: 200

status

200 get 成功 201 post 成功 301 永久重定向 302 临时重定向 304 协商缓存 服务器文件未修改 400 客户端请求有语法错误,不能被服务器识别 403 服务器受到请求,但是拒绝提供服务,可能是跨域 404 请求的资源不存在 405 请求的method不允许 500 服务器发生不可预期的错误

发送请求的示例,以及封装一个多浏览器兼容的请求函数

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();


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

// 默认不带cookie

fetch(
    'http://domain/service', {
        method: 'GET',
        credentials: 'same-origin'
    }
)

// 错误不会reject
// HTTP错误(例如404 Page Not Found 或 500 Internal Server Error)不会导致Fetch返回的Promise标记为reject;.catch()也不会被执行。
// 想要精确的判断 fetch是否成功,需要包含 promise resolved 的情况,此时再判断 response.ok是不是为 true

fetch(
        'http://domain/service', {
            method: 'GET'
        }
    )
    .then(response => {
        if (response.ok) {
            return response.json();
        }
        throw new Error('Network response was not ok.');
    })
    .then(json => console.log(json))
    .catch(error => console.error('error:', error));

// 不支持直接设置超时, 可以用promise
function fetchTimeout(url, init, timeout = 3000) {
    return new Promise((resolve, reject) => {
        fetch(url, init)
            .then(resolve)
            .catch(reject);
        setTimeout(reject, timeout);
    })
}

// 中止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();
interface IOptions {
    url: string;
    type?: "GET" | "POST";
    data: any;
    timeout?: number;
}

function formatUrl(object) {
    // a=xxx&b=xxxx; querystring
    let dataArr = [];

    for (let key in object) {
        dataArr.push(`${key}=${encodeURIComponent(object[key])}`);
    }
    return dataArr.join("&");
}

export function ajax(
    options: IOptions = {
        type: "GET",
        data: {},
        timeout: 3000,
        url: "",
    }
) {
    return new Promise((resolve, reject) => {
        if (!options.url) {
            return;
        }

        const queryString = formatUrl(options.data);

        const onStateChange = () => {
            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);
                    }
                }
            };
        };

        let timer;
        let xhr;

        if ((window as any).XMLHttpRequest) {
            xhr = new XMLHttpRequest();
        } else {
            xhr = new ActiveXObject("Microsoft.XMLHTTP");
        }

        if (options.type.toUpperCase() === "GET") {
            xhr.open("GET", `${options.url}?${queryString}`);
            onStateChange();
            xhr.send();
        } else if (options.type.toUpperCase() === "POST") {
            xhr.open("POST", options.url);
            xhr.setRequestHeader(
                "ContentType",
                "application/x-www-form-urlencoded"
            );
            onStateChange();
            xhr.send(options.data);
        }

        if (options.timeout) {
            timer = setTimeout(() => {
                xhr.abort();
                reject("timeout");
            }, options.timeout);
        }
    });
}