JavaScript BOM学习笔记

BOM(Browser Object Model)

window对象

BOM的核心是window对象,表示浏览器实例

window对象在浏览器中有两重身份,一个是es中的global对象,另一个是浏览器窗口的js接口

网页中定义的所有对象、变量和函数都以window作为其Global对象,都可以访问其上定义的全局方法

Global作用域

所有通过var声明的全局变量和函数都会称为window对象的属性和方法

窗口关系

top:始终指向最上层窗口(最外层),即浏览器窗口本身

parent:始终指向当前窗口的父窗口

self:始终指向window,实际上self和window就是同一个对象

window.top、window.parent、window.self

窗口位置与像素比

screenTop、screenLeft可以确定窗口相对于屏幕的位置,返回像素

可以使用moveTo(x, y)和moveBy(x, y)方法移动窗口,前者接收要移动到位置的绝对坐标,后者接收要移动的像素

像素比

window.devicePixelRatio表示物理像素与逻辑像素之间的缩放系数

窗口大小
window.outerWidth/outerHeight

​ 返回浏览器窗口自身大小(不管是在最外层window上使用还是对<frame>使用)

window.innerWidth/innerHeight

​ IE8以及IE8以下不兼容

​ 返回浏览器窗口中页面视口的大小

document.documentElement.clientWidth/clientHeight

​ 标准模式下正常使用,任意浏览器都兼容

​ 返回页面视口的宽高

document.body.clientWidth/clienHeight

​ 怪异模式

浏览器窗口的精确尺寸不好确定,但是可以确定页面视口大小

封装getViewportOffset方法

​ document.compatMode返回当前浏览器模式:BackCompat(向后兼容)、CSS1Compat(标准模式)

function getViewportOffset() {
    if (window.innerWidth) {
       return {
           w: window.innerWidth,
           h: window.innerHeight
       }
    } else {
        if (document.compatMode === "BackCompat") {
            return {
                w: document.body.clientWidth,
                h: document.body.clientHeight
            }
        } else {
            return {
                w: document.documentElement.clientWidth,
                h: document.documentElemnet.clientHeight
            }
        }
    }
}
resizeTo()、resizeBy()

​ 调整窗口大小,这两个方法都接受两个参数

​ 前一个方法表示要缩放到的宽高,后者表示要缩放的宽高

视口位置
window.pageXOffset(scrollX)/pageYOffset(scrollY)

​ 查看滚动条的滚动距离,IE8以及以下不兼容

document.body.scrollLeft/scrollTop、document.documentElement.scrollLeft/scrollTop

​ 兼容IE8以及IE8以下的浏览器,但是这两个属性兼容性混乱,使用的时候将俩值相加,因为不会存在俩值同时存在

封装getScrollOffset方法
function getScrollOffset() {
    if (window.pageXOffset) {
        return {
            x: window.pageXOffset,
            y: window,pageYOffset
        }
    } else {
        return {
            x: document.body.scrollLeft + document.documentElement.scrollLeft,
            y: document.body.scrollTop + document.documentElement.scrollTop
        }
    }
}

让滚动条滚动

1、window.scroll()
2、window.scrollTo()
3、window.scrollBy()

前两个方法类似,都是传入x,y坐标,使滚动条滚动到当前位置;第三个会在之前的数据上累加x,y

这几个方法也接收一个ScrollToOptions字典,除了提供偏移值,还可以通过behavior属性告诉浏览器是否平滑移动

window.scroll({
    left: 100,
    top: 100,
    behavior: 'auto'
});

//平滑滚动
window.scroll({
    left: 100,
    top: 100,
    behavior: 'smooth'
});
导航与打开新窗口

window.open()

该方法可以用于导航到指定的URL,一可以用于打开新窗口

该方法接收四个参数:要加载的URL、目标窗口、特性字符串、表示新窗口在浏览器历史记录中是否替代当前加载页面的布尔值

通常只用前三个参数,最后一个参数只有在不打开新窗口时才会使用

如果第二个参数是一个已经存在的窗口或窗格(frame)的名字,则会在对应的窗口或窗格中打开URL

弹出窗口

如果window.open第二个参数不是已有窗口,则会打开一个新窗口或标签页,第三个参数即特性字符串用于指定新窗口的配置,如果不传,则新窗口默认为所有浏览器的默认特性;如果打开的不是新窗口则忽略第三个参数

特性字符串是逗号分隔的设置字符串,指定新窗口的特性,有很多键值:

**yes/no:**fullscreen(新窗口是否最大化,仅IE支持)、location(是否显示地址栏)、Menubar(是否显示菜单栏)、resizable(是否允许拖动改变新窗口大小)、scrollbars(是否在内容过长时滚动)、status(是否显示状态栏)、toolbar(是否显示工具栏)

一般默认属性都为no

**数值:**height(不能小于100)、left(不能是负值)、top(不能是负值)、width(不能小于100)

window.open返回一个新建窗口的引用,与普通window对象没有区别,方便控制新窗口

还可以通过xinWindow.close()来关闭新窗口,但是关闭后只能检查xinWindow的closed属性了

let xinWindow = window.open(...);xinWindow.close();alert(xinWindow.closed);	//true

创建新窗口的window对象有一个opener属性,指向打开它的窗口,这个属性只在弹出窗口的最上层window对象(top)有定义

可以将新打开的标签页的opener设置为null,这样就切断了当前页与标签页的通讯,表示新标签页可以独立运行,但是这样切断后就不能再恢复了

安全限制

各种浏览器对弹出窗口进行了诸多限制,限制不一

弹窗屏蔽程序

大多数浏览器都有内置的弹窗屏蔽程序,要检测弹窗是否被屏蔽,可以通过window.open的返回值和try/catch(因为扩展程序和其他程序屏蔽弹窗时,window.open通常会报错)来实现

let blocked = false;
try {
    let xinWindow = window.open(...);
    if (xinWindow == null) {
        blocked = true;
    }
} catch(e) {
    blocked = true;
}
if (blocked == true) {
    alert('the popup is blocked');
}
定时器

前文es中有涉及,所以这里不涉及使用方法

因为js是单线程的,所以每次只能执行一段代码,为了调度不同代码执行,js维护了一个任务队列,其中的任务会按照添加到队列中的先后顺序执行

setTimeout的第二个参数只是告诉js引擎在指定毫秒数后把任务添加到这个队列,但是不保证立即执行,因为此时队列不一定是空的;所以才有了上文说的那种时间不准的情况

setInterval的第二个参数只是告诉js引擎在循环的指定毫秒数后把任务添加到这个队列,但是不保证立即执行,因为此时队列不一定是空的;所以才有了上文说的那种时间不准的情况

setTimeout会返回一个超时排期的数值ID、setInterval会返回一个循环定时ID,这两个ID都能用于取消相关任务

系统对话框

alert、confirm、promt方法,可以调用浏览器系统对话框

alert()——警告框:只接收一个参数,一般是字符串,如果不是则调用toString方法将其转换为字符串

confirm()——确认框:与警告框接收参数一样,但是可以通过其返回值判断用户是点击了确认按钮还是取消按钮

if (confirm("are you sure?")) {} else {}

promt()——提示框:接收俩个参数,第一个是展示给用户的文本,第二个是文本输入框的默认值(可以是空字符串);如果用户点击了确认按钮,则返回输入框内的值,如果点击了取消按钮则返回null

print()——打印对话框:无返回值,通过在window对象上调用;异步,所以不会阻塞代码执行,但是代码也不能知道用户的相关操作,用户禁用弹框对其没有影响

find()——查找对话框:无返回值,通过在window对象上调用;异步,所以不会阻塞代码执行,但是代码也不能知道用户的相关操作,用户禁用弹框对其没有影响

location对象

location对象是BOM最有用的对象之一,提供了当前窗口中加载文档信息,以及通常的导航功能

它既是window的属性也是document的属性,window.location和document.location指向同一个location

它还储存了URL解析后的信息

假设URL是:http://foouser:barpassword@www.wrox.com:80/WileyCDA/?q=javascript#contents

属性说明
location.hash“#contents”URL散列值(井号后面跟0或者多个字符),如果没有则为空字符串
location.host“www.wrox.com:80”服务器名以及端口号
location.hostname“www.wrox.com”服务器名
location.href“http://foouser:barpassword@www.wrox.com:80/WileyCDA/?q=javascript#contents”当前页面完整的URL(location的toString)
location.pathname“/WileyCDA/”URL中的路径和(或)文件名
location.port“80”请求的端口,如果没有则返回空字符串
location.protocol“http:”页面使用的协议
location.search“?q=javascript”URL的查询字符串,以问号开头
location.username“foouser”域名前指定的用户名
location.password“barpassword”域名前指定的密码
location.origin“http://www.wrox.com”URL的源地址(只读)
查询字符串

URLSearchParams提供了一组标准API方法,通过它们可以检查和修改字符串

向URLSearchParams构造函数传入一个查询字符串,就可以创建一个实例,这个实例暴露了get()、set()、delete()方法,可以对查询字符串执行相应的操作

大多数实现了URLSearchParams的浏览器也支持迭代其实例

let params = new URLSearchParams("?q=javascript&num=10");params.toString();	//"q=javascript&num=10"params.has("num");	//trueparams.get("num");	//10params.set("page", "3");params.toString();	//"q=javascript&num=10&page=3"params.delete('q');for (let param of params) {    console.log(param);}//["num", "10"]//["page", "3"]
操作地址

可以通过location对象修改浏览器地址,最常用的方法是assign()方法

location.assign("http://www.wrox.com");

它会立即启动导航到新URL操作,同时在浏览器历史记录中增加一条记录,如果给location.href和window.location设置一个URL也会以这个URL调用assign方法

修改其他location属性(除了hash值)都会导致URL改变,而且页面会重新加载

location.replace()方法将不会增加历史记录,也就是页面刷新后不能返回前一个页面了

location.reload()方法会重新加载当前页面;如果不传参数,则页面会以最有效的方式重新加载,比如直接从缓存中读取(从上次加载后就未修改过);如果想从服务器重新加载,可以传一个true给reload()函数(脚本中位于reload方法后的代码可能执行也可能不执行,这取决于网络延迟和系统资源的问题)

navigator

navigator最早是由Netscape Navigator2引入的,现在已经成为客户端标识浏览器的标准

只要启动浏览器JavaScript,navigator对象就一直存在

navigator属性方法有很多,详情参见红宝书p375

检测插件

除IE10以及更低版本外的浏览器,都可以通过plugins数组来确定浏览器是否安装了插件

数组中的每一项都包含几个属性:name、description、filename、length(由当前插件处理的MIME类型数量)

其实每个数组对象中还有一个MineType属性,只能通过中括号访问,每个MineType有四个属性:description(描述MIME类型)、enabledPlugin(指向插件对象的指针)、suffixes(该MIME类型对应扩展名的逗号分隔的字符串)、type(完整的MIME类型字符串)

let hasPlugin = function(name) {    name = name.toLowerCase();    for (let plugin of window.navigator.navigator.plugins) {        if (plugin.name.toLowerCase().indexOf(name) > -1) {            return true;        }    }    return false;}hasPlugin("Flash");	//true//旧版本IE检测方式function hasIEPlugin(name) {    try {        new ActiveXObject(name);        return true;    } catch(e) {        return false;    }}hasIEPlugin("QuickTime.QuickTime");

plugins还有一个refresh()方法用于刷新plugins属性以反映新安装的插件,该方法接受一个布尔值,表示是否重新加载页面;如果传入true则所有包含插件的页面都要重新加载;否则,只有plugins会更新,但页面不会重新加载

注册处理程序

现代浏览器支持navigator上的registerProtocolHandler()方法,这个方法可以把一个网站注册为处理某种特定类型信息应用程序

可以借助这个方法将Web应用程序注册为像桌面软件一样的默认应用程序

该方法接收三个参数:要处理的协议(如“mailto”)、处理该协议的url、应用名称

screen对象

不常用的对象,这个对象中保存的是客户端能力信息,也就是了浏览器窗口外客户端显示器信息,例如像素宽度和像素高度

每个浏览器都会在screen对象上暴露不同属性

详情请参阅红宝书p379

history对象

表示当前窗口首次使用以来用户的导航记录,每个window都有自己的history对象

history对象不会暴露url,只能前进或后退

导航

go()方法可以向任何方向导航,向前或向后都行,该方法接收一个参数,正数表示在历史记录中向前,负数表示在历史记录中向后,如果是字符串则导航到最近的url包含该字符串的位置

forward()、back()模拟浏览器前进和后退按钮

length:表示历史记录中有多少条目(2009年以来发布的主流浏览器,改变URL散列值也会增加一条记录)

历史状态管理

history.pushState()方法,这个方法接收三个参数:state对象(只包含可被序列化的信息)、新状态标题、相对URL(可选)

history.pushState({foo:'bar'}, "title", "baz.html");

state对象大小有限制:500KB-1MB以内

触发pushState会创建新的历史记录,所以会相应的启用”后退“按钮,如果点击就会触发window对象上的popstate事件,该事件对象有一个state属性,包含pushState传入的第一个参数

window.addEventListener("popstate", (event) => {    let state = event.state;    if (state) {//第一个页面加载时状态是null        processState(state);    }})

同样可以用history.replaceState()方法来更新状态,该方法接收两个参数,这两个参数和pushState方法前两个参数一样

该方法不会创建新的历史记录,只会覆盖当前状态

客户端检测

能力检测

能力检测又称为特性检测,即在js运行时使用一套简单的逻辑,测试浏览器是否支持某种特性

if (object.propertyInQuestion) {    //使用object.propertyInQuestion}

这种方法应该先检测通用方法,再对个别方法进行检测

安全能力检测

因为有可能某个对象的属性名正好是所需要检测的属性名,但是它又不是我们真正需要的属性,只是名字重复而已,这时能力检测就拉垮了

这时我们需要检测该属性的类型以便进一步确认

function isSortable(obj) {    return typeof obj.sort == "function";}

但是IE又有问题了,因为在IE8以及更低版本里,DOM是通过COM实现的,所以:

function isCreateElement(obj) {    return typeof obj.createElement == "function";}//这个在IE8以及更低版本里typeof obj.createElement返回object,因为它被实现为COM对象
基于能力检测进行浏览器分析

使用能力检测可以精准的分析运行代码的浏览器,而且伪造用户代理字符串很简单,但是伪造能够欺骗能力检测的浏览器特性却很难

检测特性

最好集中检测所有能力,而不是等到要用的时候再重复检测,例如:

//检测浏览器是否支持Netscape式的插件let hasNSPlugins = !!(navigator.plugins && navigator.plugins.length);
检测浏览器

根据浏览器特性的检测并与已知特性对比,确认浏览器;这套方法可能在未来浏览器版本中不适用

class BrowserDetector {    constructor() {        //测试条件编译,IE6-10支持        this.isIE_Gte6Lte10 = /*@cc_on!*/false;                //测试documentMode,IE7-11支持        this.isIE_Gte7Lte11 = !!document.documentMode;                //测试StyleMedia构造函数,Edge20及以上版本支持        this.isEdge_Gte20 = !!window.StyleMedia;                //测试Firefox专有扩展安装API,所有Firefox都支持        this.isFirefox_Gte1 = typeof InstallTrigger !== 'undefined';                //测试chrome对象以及webstore属性,opera有些版本有window.chrome但是没有window.chrome.webstore,所有的chrome都支持        this.isChrome_Gte1 = !!window.chrome && !!window.chrome.webstore;                //Safari早期版本会给构造函数的标签符追加“Constructor”字样,Safari3-9.1支持        this.isSafari_Gte3Lte9_1 = /Constructor/i.test(window.Element);                //推送通知API暴露在window对象上,Safari7.1及以上版本支持        this.isSafari_Gte7_1 = (({pushNotification = {}} = {}) => pushNotification.toString() == '[object SafariRemoteNotification]')(window.safari);                //测试addons属性,Opera20及以上版本支持        this.isOpera_Gte20 = !!window.opr && !!window.opr.addons;    }        isIE() {return this.isIE_Gte6Lte10 || this.isIE_Gte7Lte11}    isEdge() {return this.isEdge_Gte20 && !this.isIE()}    isFirefox() {return this.isFirefox_Gte1}    isChrome() {return this.isChrome_Gte1}    isSafari() {return this.isSafari_Gte3Lte9_1 || this.isSafari_Gte7_1}    isOpera() {return this.isOpera_Gte20}}

随着浏览器变迁可以不断调整底层逻辑

能力检测的局限

能力检测往往不具备全面性,可能这个浏览器的特性功能别的浏览器也实现了

能力检测适合决定下一步怎么做,而不能用来作为决定浏览器的标识

用户代理检测

用户代理检测通过浏览器用户代理字符串确定使用什么浏览器,用户代理字符串包含在每个HTTP请求的头部,在js中可以通过navigator.userAgent访问

在很长一段时间里,浏览器都通过在用户代理字符串中错误的或误导信息来欺骗服务器,这背后的原因需要了解自Web出现后用户代理字符串的历史(红宝书p386-p392)

浏览器分析

毕竟用户代理是可以伪造的,有些浏览器提供了某些私有方法,对付这种造假是吃力不讨好的事

我们从用户代理可以获取相关环境信息:浏览器、浏览器版本、渲染引擎、设备类型…

GitHub上有相关第三方用户代理解析程序,这里不建议自己手写

软件与硬件检测

现代浏览器提供了一组与页面执行环境相关的信息,这些属性可以通过暴露在window.navigator上的一组API获得,但是这些API跨浏览器支持不是很好

强烈建议在使用前先检测相关它们是否存在

识别浏览器与操作系统
navigator.oscpu

返回操作系统/系统架构信息字符串

navigator.vendor

返回浏览器开发商信息字符串

navigator.platform

返回操作系统字符串

screen.colorDepth、pixelDepth

返回显示器每像素的位深

screen.orientation

返回ScreenOrientation对象,其中包含Screen Orientation API定义的屏幕信息,最有意思的属性是angle和type,前者返回默认状态下屏幕的角度,后者返回四值之一:portrait-primary、portrait-secondary、landscape-primary、landscape-secondary

浏览器元数据
Geolocation API

navigator.geolocation属性暴露了Geolocation API,可以让浏览器脚本感知当前设备的地理位置,这个API只在安全执行环境(通过HTTPS获取的脚本)中可用

这个API可以查询宿主系统并尽可能地精确的返回设备的位置信息;根据宿主系统的硬件和配置,返回结果的精度可能不一样

根据Geolocation API规范:地理位置信息主要来源是GPS和IP地址、频射识别(RFID)、WIFI及蓝牙Mac地址、GSM/CDMA蜂窝ID以及用户输入等信息

可以使用getCurrentPosition()方法获取浏览器当前的位置,这个方法返回一个coordinates对象,其中包含的信息不一定依赖宿主系统的能力:

navigator.geolocation.getCurrentPosition((position) => p = position);p.timestamp;	//查询时间的时间戳p.coords;	//Coordinates {...}p.coords.latitude; p.coords.longitude; p.coords.accuracy;	//经度,纬度,精度(单位:m)p.coords.altitude; p.coords.altitudeAccuracy;	//海拔,精度(单位:m)[需要GPS或高度计]p.coords.speed; p.coords.heading;	//每秒移动速度,相对于正北方向移动的角度(0<=heading<360)[需要加速计或指南针]

获取位置数据不一定成功,getCurrentPosition()接收第失败回调函数作为第二个参数,该参数是一个PositionError对象,会包含两个属性:code(整数)、message(错误的简短描述)

navigator.geolocation.getCurrentPosition(() => {}, (e) => {console.log(e.code);console.log(e.message);})//有三种错误//PERMISION_DENIED:浏览器未被允许访问设备位置;要么是用户不同意授权,要么就是在不安全的环境下访问了Geolocation API//POSITION_UNAVAILABLE:系统无法返回任何位置信息;//TIMEOUT:系统不能在规定时间内返回位置信息

Geolocation API位置请求可以通过PositionOptions对象来配置,作为第三个参数;该对象可以配置三个属性:

​ enableHighAccuracy:布尔值,true表示返回位置尽量精确,默认为false

​ timeout:毫秒,表示等待位置返回的最长时间,默认为2^23 - 1

​ maximumAge:毫秒,表示返回坐标的最长有效期,默认为0

Connection State、NetworkInformation API

浏览器会跟踪网络连接状态并以两种方式暴露这些信息:连接事件和navigator.onLine属性

当设备连网时,浏览器会记录这个事实并触发window对象上的online事件;当设备断开网络连接后,浏览器会触发window对象上的offline事件;任何时候都可以通过navigator.onLine属性来确定浏览器连网状态,该属性返回一个布尔值

const connectionStateChange = () => console.log(navigator.onLine);window.addEventListener("online", connectionStateChange);window.addEventListener("offline", connectionStateChange);

有些浏览器认为连接到局域网就算”在线“、

navigator还暴露了NetworkInformation API,可以通过navigator.connection属性使用,该API提供了一些只读属性:

​ downlink:整数,表示当前设备的带宽(以Mbit/s为单位),可能根据历史网络吞吐量或者连接技术能力计算

​ downlinkMax:整数,表示当前设备最大下行带宽(以Mbit/s为单位),根据网络的第一跳来决定

​ effectiveType:字符串枚举值,连接速度和质量

​ slow-2g(往返时间>2000ms,下行带宽<50kbit/s)、2g(1400ms<=往返时间<2000ms,50kbit/s<=下行带宽<70kbit/s)、3g (…)、4g(…)

​ rtt:毫秒,表示网络实际的往返时间,可能根据历史网络吞吐量或者连接技术能力计算

​ type:字符串枚举值,表示网络连接技术

​ bluetooth、cellular、ethernet、none、mixed、other、unknown、wifi、wimax

​ saveData:布尔值,表示用户是否启用了“节流”模式

​ onchange:事件处理程序,会在任何连接状态变化时激发一个change事件

Battery Status API

浏览器可以访问设备电池和充电状态的信息,navigator.getBattery()会返回一个期约实例,解决为一个BatteryManager对象

navigator.getBattery().then((b) => console.log(b));	//BatteryManager {...}

BatteryManager提供四个只读属性:

​ charging:布尔值,表示设备是否在充电

​ chargingTime:整数,表示距离充满还有多少秒

​ dischargingTime:整数,表示预计电池耗尽还有多少秒

​ level:浮点数,表示电量百分比

这个API还提供了四个事件属性:

​ onchargingchange:充电状态变化时的处理程序

​ onchargingtimechange:充电时间变化的处理程序

​ ondischargingtimechange:放电时间变化的处理程序

​ onlevelchange:电量百分比变化的处理程序

navigator.getBattery().then((battery) => {    const handler = () => console.log('xxx');	battery.onXXXX = handler;    //or    battery.addEventListener('XXXX', handler);})
硬件

浏览器硬件检测能力相当有限

处理器核心数

navigator.hardwareConcurrency,(无法确定,则这个值为1)这个值表示浏览器可以并行执行的最大工作线程数量,不一定是实际的CPU核心数

设备内存大小

navigator.deviceMemory,返回大致的系统内存大小,包含单位为GB的浮点数

最大触点数

navigator.maxTouchPoints,返回屏幕支持的最大关联触点数量

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Samuel_luo。

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值