JS - DOM
基本语法
(0)有些HTML属性名是JavaScript的保留字,转为JavaScript属性时,必须改名。主要是以下两个。
- for属性改为htmlFor
- class属性改为className
若要在HTML元素上附加数据,供JavaScript脚本使用
- 自定义属性:可能不符合标准,导致网页代码校验不通过
- 标准 data-* 属性:利用元素节点对象的dataset属性
注:data-后面的属性名只能包含小写字母、数字、连词线(-)、点(.)、冒号(:)和下划线(_)
(1)判断一个节点有无子节点,三种方法
node.hasChildNodes()
node.firstChild !== null
node.childNodes && node.childNodes.length > 0
例:结合firstChild
属性和nextSibling
属性,遍历当前节点的所有后代节点
function DOMComb(parent, callback) { if (parent.hasChildNodes()) { for (var node = parent.firstChild; node; node = node.nextSibling) { DOMComb(node, callback); } } callback(parent); } // 用法 DOMComb(document.body, console.log)
(2)removeChild
该方法是在当前结点的父节点上调用的。
例:移除当前结点的所有子节点
var element = document.getElementById('xxx'); while (element.firstChild) { element.removeChild(element.firstChild); }
结点的children属性是一个只读属性,随子节点的变化会实时更新。
在删除多个节点时,务必注意children属性时刻都在变化。
(3)compareDocumentPosition()
七个比特位的二进制值,表示参数节点与当前节点的关系
000000 0 两个节点相同 000001 1 两个节点不在同一个文档(即有一个节点不在当前文档) 000010 2 参数节点在当前节点的前面 000100 4 参数节点在当前节点的后面 001000 8 参数节点包含当前节点 010000 16 当前节点包含参数节点 100000 32 浏览器内部使用
最终结果需要结合比特位运算。
(4)结点集合
NodeList 实例是一个类数组对象,可转化为真正的数组,支持for和foreach遍历
var children = document.body.childNodes; var nodeArr = Array.prototype.slice.call(children);
NodeList实例可能是动态集合,也可能是静态集合。
动态集合是一个活的集合,DOM删除或新增一个相关节点,会立刻反映在NodeList实例上。
注:目前只有node.childNodes返回的NodeList是动态集合。
- childNodes:所有子结点
- children:子元素结点
其他返回NodeList的方法
- document.getElementsByName
- Element.querySelectorAll
HTMLCollection 实例是一个类数组对象,只包含元素(Element)结点,只支持for遍历,动态集合。
- document.links
- document.forms
- document.images
- document.scripts
- document.embeds,document.plugins
以及元素定位方法
- document.getElementsByTagName
- document.getElementsByClassName
document.styleSheets属性返回文档内嵌或引入的样式表集合,类型StyleSheetList。
(5)HTML表单
常用标签
文本框:<input type="text">,用于输入文本; 口令框:<input type="password">,用于输入口令; 单选框:<input type="radio">,用于选择一项; 复选框:<input type="checkbox">,用于选择多项; 下拉框:<select>,用于选择一项; 多行文本框:<textarea>,定义多行的文本输入控件 隐藏文本:<input type="hidden">,用户不可见,但表单提交时会把隐藏文本发送到服务器。 文件选择:<input type="file">:HTML表单中用于上传文件
当一个表单包含<input type="file">时,表单的enctype必须为multipart/form-data,method必须为post,浏览器才能正确编码并以multipart/form-data格式发送表单数据。
(6)根据name属性获取标签结点
document.querySelectorAll('*[name="xxx"]'); document.getElementsByName("xxx");
注意,没有name属性的<input>的数据不会被提交。
(7)网页属性
a. 整张网页的总高度/宽度
document.documentElement.scrollHeight/scrollWidth document.body.scrollHeight/scrollWidth
b. 整张网页的水平的和垂直的滚动距离
document.documentElement.scrollLeft document.documentElement.scrollTop
注:该属性都可读写,设置该属性的值,会导致浏览器将当前元素自动滚动到相应的位置。
c. 计算元素左上角相对于整张网页的坐标
function getElementPosition(e) { var x = 0; var y = 0; while (e !== null) { x += e.offsetLeft; y += e.offsetTop; e = e.offsetParent; } return {x: x, y: y}; }
document
获取document对象的方法
- 正常的网页,直接使用
document
或window.document
iframe
框架里面的网页,使用iframe
节点的contentDocument
属性- Ajax 操作返回的文档,使用
XMLHttpRequest
对象的responseXML
属性 - 内部节点的
ownerDocument
属性
document对象通常有2个子结点
(1)document.doctype:指向<DOCTYPE>
节点,通常为<!DOCTYPE html>,与document.firstChild等效
(2)document.documentElement:指向当前文档的根节点,通常为<html>
document.head和document.body属性分别直接指向<head>节点和<body>节点。
状态属性
document.visibilityState
该属性返回文档的可见状态:
- visible:页面可见。页面可能是部分可见,即不是焦点窗口,前面被其他窗口部分挡住了
- hidden: 页面不可见,有可能窗口最小化,或者浏览器切换到了另一个Tab页面
- prerender:页面处于正在渲染状态,对于用于来说,该页面不可见
- unloaded:页面从内存里面卸载了
应用场景:
- 页面加载时,防止加载某些资源
- 页面不可见时,停掉一些页面功能
document.readyState
该属性返回当前文档的状态:
loading
:加载HTML代码阶段(尚未完成解析)interactive
:加载外部资源阶段complete
:加载完成
页面加载过程:
- 浏览器开始解析HTML文档,
document.readyState
属性等于loading
- 浏览器遇到HTML文档中的
<script>
元素,并且没有async
或defer
属性,就暂停解析,开始执行脚本,这时document.readyState
属性还是等于loading
- HTML文档解析完成,
document.readyState
属性变成interactive
- 浏览器等待图片、样式表、字体文件等外部资源加载完成,一旦全部加载完成,
document.readyState
属性变成complete
方法
document.createTextNode()
用于生成文本节点(Text实例)。
该方法可以确保返回的节点,被浏览器当作文本渲染,而不是当作HTML代码渲染。因此,可以用来展示用户的输入,避免 XSS 攻击。
var div = document.createElement('div'); div.appendChild( document.createTextNode('<span>Foo & bar</span>') ); console.log(div.innerHTML) 结果:<span>Foo & bar</span>
会对大于号和小于号进行转义,保证即使用户输入的内容包含恶意代码,也能正确显示。
但该方法不对单引号和双引号转义,所以不能用来对HTML属性赋值。
document.createDocumentFragment()
该方法生成一个空的存在于内存中的文档片段对象。
新对象不属于当前文档,常用来生成一段较复杂的DOM结构,然后再插入当前文档,避免直接修改当前DOM引发网页的重新渲染。
该方法等同于浏览器原生的DocumentFragment构造函数
var docFrag = new DocumentFragment();
注意,DocumentFragment节点本身不能被插入当前文档,而是其所有子结点插入当前文档中。插入完成后,该DocumentFragment对象变为空结点(textContent属性为空串),可以被再次使用。
若想保留其子结点,可以通过cloneNode()方法
node.appendChild(docFrag.cloneNode(true));
document.createNodeIterator()
该方法返回一个子节点遍历器对象(NodeFilter实例)
var nodeIterator = document.createNodeIterator( document.body, //要遍历的根节点 NodeFilter.SHOW_ELEMENT //要遍历的节点类型 );
注:遍历器返回的第一个节点,总是根节点。
- nextNode:先返回遍历器指向的节点A,然后将指针移到下一个节点B
- previousNode:先将指针移到上一个节点D,然后返回该节点D
var currentNode = nodeIterator.nextNode(); var previousNode = nodeIterator.previousNode(); currentNode === previousNode //true
document.createTreeWalker()
该方法返回一个DOM的子树遍历器对象(TreeWalker实例)
注:遍历器返回的第一个节点,不是根节点。
var treeWalker = document.createTreeWalker( document.body, //要遍历的根节点 NodeFilter.SHOW_ELEMENT //要遍历的节点类型 );
相同条件下,treeWalker 比 nodeIterator 少一个根节点。
Element
属性
innerHTML VS textContent VS innerText
- 读属性值时,若文本节点包含 &、<、> ,
innerHTML
属性会转义为实体形式 &、<、> 。若想得到原文,建议使用element.textContent
属性。 - 写属性值时,不存在上述转义问题;
- 为了安全考虑,如果插入的是文本,优先
textContent
属性;
方法
Element.insertAdjacentHTML()
将一个HTML字符串,解析生成DOM结构(转义字符),插入相对于当前节点的指定位置。
- 执行速度比innerHTML属性快得多
- 不转义HTML字符串,导致不能用来插入用户输入的内容,否则会有安全风险
Element.closest()
接受一个CSS选择器作为参数,返回匹配该选择器的、最接近当前节点的一个祖先节点(包括当前节点本身)。
如果没有任何节点匹配 CSS 选择器,则返回null。
Element.getBoundingClientRect()
返回一个rect对象,提供当前元素节点的大小、位置等信息(CSS盒状模型的所有信息)
x:元素左上角相对于视口的横坐标 y:元素左上角相对于视口的纵坐标 /// 元素本身 + padding + border height:元素高度 width:元素宽度 /// 随着页面滚动变化而变动 left:元素左上角相对于视口的横坐标,与x属性相等 right:元素右边界相对于视口的横坐标(等于x + width) top:元素顶部相对于视口的纵坐标,与y属性相等 bottom:元素底部相对于视口的纵坐标(等于y + height)
若要得到绝对值,可以将left
属性加上window.scrollX
,top
属性加上window.scrollY
rect
对象没有自身属性,全部继承于原型属性,所以:Object.keys(rect) // []
另外,Element.getClientRects 方法返回类数组对象,与Element.getBoundingClientRect()相比,该方法:
- 返回对象有多少个成员,取决于该元素在页面上占据多少行
- 主要用于判断行内元素是否换行,以及行内元素的每一行的位置偏移
- 考虑行内元素的换行符
此外,对于页面元素的焦点问题,考虑以下属性和方法
Element.focus() Element.blur() document.activeElement /// 让xxx元素获得焦点,并滚动到可见区域 function getFocus() { document.getElementById('xxx').focus({preventScroll:false}); }
DOM事件
事件驱动编程模式(event-driven),通过监听函数对事件做出反应。 所有DOM的事件操作(监听和触发),都定义在EventTarget接口
- addEventListener:绑定事件的监听函数
- removeEventListener:移除事件的监听函数
- dispatchEvent:触发指定事件
若向监听函数传递参数,可用匿名函数包装监听函数
btn.addEventListener('click', function () { alert(this.nodeName); //监听函数内部的this,指向当前事件所在的那个对象 fun('Hello'); }, false);
其中,false标志冒泡阶段,true标志捕获阶段,默认false。
事件绑定
JS提供三种为事件绑定监听函数的方法:
- HTML的on-属性:冒泡阶段触发,值为函数立即执行代码
el.setAttribute('onclick', 'doSomething()'); // 等同于 <Element onclick="doSomething()">
违反了HTML与JavaScript代码相分离的原则。
- 元素节点对象的事件属性:冒泡阶段触发,值为函数名
div.onclick = function (event) { console.log('触发事件'); };
缺点是同一个事件只能定义一个监听函数。
- EventTarget.addEventListener()
推荐使用
- 同一个事件可以添加多个监听函数
- 能够指定在哪个阶段(捕获阶段还是冒泡阶段)触发监听函数
- 除了DOM节点,其他对象(比如
window
、XMLHttpRequest
等)也有这个接口,它等于是整个JavaScript统一的监听函数接口
事件传播
propagation,事件传播的最上层对象是window,接着依次是document,html(document.documentElement)和body(document.body)
- 第一阶段:(始于window,由外向内)从window对象传到目标节点(上层传到底层),称为“捕获阶段”(capture phase)
- 第二阶段:在目标节点上触发,称为“目标阶段”(target phase)
- 第三阶段:(由内向外,终于window)从目标节点传回window对象(从底层传回上层),称为“冒泡阶段”(bubbling phase)
注意,浏览器总是假定click事件的目标节点,是点击位置嵌套最深的那个节点。
对事件传播的详细讲解过程,参见:JS教程 - 事件模型 的propagation部分。
事件代理
delegation,把监听函数定义在父节点上,由父节点的监听函数统一处理多个子元素的事件。
可以通过事件对象的stopPropagation方法阻止事件的传播
// 事件传播到 node 元素后,就不再向下传播了 node.addEventListener('click', function (event) { event.stopPropagation(); }, true); ///捕获阶段 // 事件冒泡到 node 元素后,就不再向上冒泡了 node.addEventListener('click', function (event) { event.stopPropagation(); }, false); ///冒泡阶段
若同时阻止node节点的其他click事件的监听函数,彻底阻止这个事件的传播,请移步:stopImmediatePropagation方法。
事件对象
浏览器原生提供一个Event对象,所有的事件都是该对象的实例,或者说继承了Event.prototype对象。
var event = new Event( 'look', { 'bubbles': true, 'cancelable': false });
其中,只读属性bubble的true标志冒泡阶段,false标志捕获阶段,默认false。
由Event构造函数生成的事件,Event.isTrusted属性返回false,标志是脚本产生的,而非真正的用户行为产生的。
除Event外,浏览器原生提供CustomEvent()构造函数,用来生成自定义的事件实例。
- 在触发事件的同时,传入指定的数据
- 数据通过其属性detail带入
Event.eventPhase
返回一个整数常量,表示事件目前所处的阶段,只读属性
- 0,事件目前没有发生
- 1,事件目前处于捕获阶段,即处于从祖先节点向目标节点的传播过程中
- 2,事件到达目标节点,即
Event.target
属性指向的那个节点 - 3,事件处于冒泡阶段,即处于从目标节点向祖先节点的反向传播过程中
Event.cancelable
返回一个布尔值,表示事件是否可以取消,只读属性,默认false
推荐用法:调用Event.preventDefault()之前,先预判
function preventEvent(event) { if (event.cancelable) { event.preventDefault(); } else { console.warn('This event can not be canceled.'); } }
Event.target VS Event.currentTarget
- target属性:返回原始触发事件的那个节点,即事件最初发生的节点
- currentTarget属性:返回事件当前所在的节点,即正在执行的监听函数所绑定的那个节点
事件传播过程中,不同节点的监听函数内部的Event.target与Event.currentTarget属性的值是不一样的,前者总是不变的,后者则是指向监听函数所在的那个节点对象。
Event.preventDefault()
取消浏览器对当前事件的默认行为,前提是事件对象的cancelable属性为true。
该方法只是取消事件对当前元素的默认影响,不会阻止事件的传播。
Event.composedPath()
返回一个数组,成员是事件的最底层节点和依次冒泡经过的所有上层节点。
事件类型
- 鼠标事件:继承MouseEvent接口
在父节点内部进入子节点,不会触发mouseenter事件,但是会触发mouseover事件;
在父节点内部离开子节点,不会触发mouseleave事件,但是会触发mouseout事件;
relatedTarget:节点对象,表示事件的相关节点,默认为null。
mouseenter和mouseover事件时,表示鼠标刚刚离开的那个元素节点;
mouseout和mouseleave事件时,表示鼠标正在进入的那个元素节点;
拓展注意,不同事件的target属性和relatedTarget属性的不同:
事件名称 target 属性 relatedTarget 属性 focusin 接受焦点的节点 丧失焦点的节点 focusout 丧失焦点的节点 接受焦点的节点 mouseenter 将要进入的节点 将要离开的节点 mouseleave 将要离开的节点 将要进入的节点 mouseout 将要离开的节点 将要进入的节点 mouseover 将要进入的节点 将要离开的节点 dragenter 将要进入的节点 将要离开的节点 dragexit 将要离开的节点 将要进入的节点
- 滚轮事件:WheelEvent接口继承MouseEvent的实例
浏览器原生提供WheelEvent()构造函数,用来生成WheelEvent实例。
var wheelEvent = new WheelEvent('wheel', options);
- 键盘事件:继承KeyboardEvent接口、Event接口
浏览器原生提供KeyboardEvent构造函数,用来新建键盘事件的实例。
属性key表示当前按下的键,默认为空字符串。
注意,如果一直按键不松开,会一直触发事件,直至松开
keydown --> keypress --> keydown --> keypress --> …(重复以上过程) --> keyup
通过KeyboardEvent.repeat属性返回一个布尔值,代表该键是否被按着不放,以便判断是否重复这个键。
- 进度事件:继承ProgressEvent接口
描述资源加载/文件上传的进度,主要由 AJAX请求、<img>、<audio>、<video>、<style>、<link> 等外部资源的加载触发。
abort:外部资源中止加载时(比如用户取消)触发。如果发生错误导致中止,不会触发该事件。 error:由于错误导致外部资源无法加载时触发。 load:外部资源加载成功时触发。 loadstart:外部资源开始加载时触发。 loadend:外部资源停止加载时触发,发生顺序排在error、abort、load等事件的后面。 progress:外部资源加载过程中不断触发。 timeout:加载超时时触发。
推荐方法,避免出现图片在脚本未执行完就加载完毕、监听函数不会执行的情况
function loaded() { // ... } if (image.complete) { loaded(); } else { image.addEventListener('load', loaded); }
注意,由于DOM元素节点没有提供是否加载错误的属性,所以error事件的监听函数最好放在<img>
元素的HTML代码中,这样才能保证发生加载错误时百分之百会执行错误提示。而且,error事件不会冒泡,子元素的error事件,不会触发父元素的error事件监听函数。
- 拖拉事件:继承DragEvent接口,同时继承MouseEvent接口和Event接口
浏览器原生提供一个DragEvent()构造函数,用来生成拖拉事件的实例对象。
关于拖拉事件,有以下几个注意点
- 某个元素节点的draggable属性设为true,就无法再用鼠标选中该节点内部的文字或子节点。
- 拖拉过程只触发以上这些拖拉事件,尽管鼠标在移动,但是鼠标事件不会触发。
- 将文件从操作系统拖拉进浏览器,不会触发
dragstart
和dragend
事件。 dragenter
和dragover
事件的监听函数,用来取出拖拉的数据(即允许放下被拖拉的元素)。由于网页的大部分区域不适合作为放下拖拉元素的目标节点,所以这两个事件的默认设置为当前节点不允许接受被拖拉的元素。如果想要在目标节点上放下的数据,首先必须阻止这两个事件的默认行为。
通过如下例子:将一个节点从当前父节点,拖拉到另一个父节点中,理解拖拉事件
/* HTML 代码如下 <div class="dropzone"> <div id="draggable" draggable="true"> 该节点可拖拉 </div> </div> <div class="dropzone"></div> <div class="dropzone"></div> <div class="dropzone"></div> */ // 被拖拉节点 var dragged; document.addEventListener('dragstart', function (event) { // 保存被拖拉节点 dragged = event.target; // 被拖拉节点的背景色变透明 event.target.style.opacity = 0.5; }, false); document.addEventListener('dragend', function (event) { // 被拖拉节点的背景色恢复正常 event.target.style.opacity = ''; }, false); document.addEventListener('dragover', function (event) { // 阻止事件的默认行为,防止拖拉效果被重置,允许被拖拉的节点放入目标节点 event.preventDefault(); }, false); document.addEventListener('dragenter', function (event) { // 目标节点的背景色变紫色 // 由于该事件会冒泡,所以要过滤节点 if (event.target.className === 'dropzone') { event.target.style.background = 'purple'; } }, false); document.addEventListener('dragleave', function( event ) { // 目标节点的背景色恢复原样 if (event.target.className === 'dropzone') { event.target.style.background = ''; } }, false); document.addEventListener('drop', function( event ) { // 防止事件默认行为(比如某些元素节点上可以打开链接), event.preventDefault(); if (event.target.className === 'dropzone') { // 恢复目标节点背景色 event.target.style.background = ''; // 将被拖拉节点插入目标节点 dragged.parentNode.removeChild(dragged); event.target.appendChild( dragged ); } }, false);
注意,drag、dragstart、dragend是针对被拖拉对象,dragenter、dragover、dragleave、drop是针对目标结点对象。
DataTransfer
所有拖拉事件的实例都有一个DragEvent.dataTransfer属性,用来读写需要拖拉传递的数据。该属性的值是一个DataTransfer接口的实例。
浏览器原生提供一个DataTransfer()构造函数,无参
var dataTrans = new DataTransfer();
拖拉事件开始时,开发者可以提供数据类型和数据值。拖拉过程中,开发者通过dragenter和dragover事件的监听函数,检查数据类型以确定是否允许放下(drop)被拖拉的对象。发生drop事件时,监听函数取出拖拉的数据即可。
属性
dropEffect VS effectAllowed
- dropEffect设置接受拖拉结点的区域的效果,effectAllowed设置本次拖拉中被拖拉的节点允许的效果;
- dropEffect在dragenter和dragover事件的监听函数中设置,effectAllowed在dragstart事件的监听函数中设置;
该2个属性是同一件事的两个方面,通常配合使用
source.addEventListener('dragstart', function (e) { e.dataTransfer.effectAllowed = 'move'; }); target.addEventListener('dragover', function (e) { ev.dataTransfer.dropEffect = 'move'; });
types VS items
- 拖拉的数据格式(通常是 MIME 值),只读数组
- 类似数组的只读对象(DataTransferItemList 实例),每个成员就是本次拖拉的一个对象(DataTransferItem 实例)
该2个属性通常在drop事件的监听函数中获取。
注:若想为拖拉事件添加数据,只能在dragstart中设置;若想从拖拉事件中获取数据,只能在drop事件监听函数中。
- 触摸事件:触摸点(Touch)、触摸点集合(TouchList)、触摸事件(TouchEvent)
TouchEvent接口继承Event接口,表示由触摸引发的事件实例
touches
:TouchList
实例,代表所有的当前处于活跃状态(触摸中)的触摸点,默认值是一个空数组[]
。targetTouches
:TouchList
实例,代表所有处在触摸的目标元素节点内部、且仍然处于活动状态的触摸点,默认值是一个空数组[]
。changedTouches
:TouchList
实例,代表本次触摸事件的相关触摸点,默认值是一个空数组[]
。
若 ev.touches.length === ev.targetTouches.length,表明所有触摸点均在目标元素结点内。
注:若想在发生触摸事件的同时阻止鼠标事件,使用:event.preventDefault方法。
- 表单事件:
// input事件(连续触发) 当<input>、<select>、<textarea>的值发生变化, 单复选框改变选项, 打开contenteditable属性的元素结点的值发生变化时触发 // select事件 在<input>、<textarea>里面选中文本时触发 // Change事件(不会连续触发) 当<input>、<select>、<textarea>的值发生变化且只有当全部修改完成时才会触发 [1]. 激活单选框(radio)或复选框(checkbox)时触发。 [2]. 用户提交时触发。比如,从下列列表(select)完成选择,在日期或文件输入框完成选择。 [3]. 当文本框或<textarea>元素的值发生改变,并且丧失焦点时触发。 // invalid 事件 用户提交表单时,如果表单元素的值不满足校验条件时触发 // reset 事件,submit 事件:发生在表单对象<form>上,而不是发生在表单的成员上 reset事件当表单重置(所有表单成员变回默认值)时触发。 submit事件当表单数据向服务器提交时触发。
- 其他事件
a. 资源事件
- beforeunload事件:资源将要卸载前触发
- unload 事件:窗口关闭或者
document
对象将要卸载时触发。它的触发顺序排在beforeunload
、pagehide
事件后面。事件发生时,所有资源依然存在,但是对用户来说都不可见,UI互动全部无效。缓存存在,则事件无效。 - load事件:页面或某个资源加载成功时触发。缓存存在,则事件无效。
推荐:兼容性好
window.addEventListener('beforeunload', function(e) { var confirmationMessage = '确认关闭窗口?'; e.returnValue = confirmationMessage; return confirmationMessage; });
b. 焦点事件
在元素节点和document对象上面触发,与获得/失去焦点相关。
focus
:元素节点获得焦点后触发,该事件不会冒泡blur
:元素节点失去焦点后触发,该事件不会冒泡focusin
:元素节点将要获得焦点时触发,发生在focus
事件之前,该事件会冒泡focusout
:元素节点将要失去焦点时触发,发生在blur
事件之前,该事件会冒泡
focusin --> focus --> focusout --> blur
这四个事件都继承FocusEvent接口,提供属性:
FocusEvent.target
:事件的目标节点FocusEvent.relatedTarget
:对于focusin
事件,返回失去焦点的节点;对于focusout
事件,返回将要接受焦点的节点;对于focus
和blur
事件,返回null
。
对于focus和blur事件,通常将addEventListener
方法的第三个参数需要设为true,因为只能在捕获阶段触发。
form.addEventListener('focus', function (event) { event.target.style.background = 'pink'; }, true); form.addEventListener('blur', function (event) { event.target.style.background = ''; }, true);
c. session历史事件
默认情况下,浏览器会在当前会话(session)缓存页面,当用户点击“前进/后退”按钮时,浏览器就会从缓存中加载页面。
- pageshow事件:页面加载时触发,包括第一次加载和从缓存加载两种情况。第一次加载时,触发顺序在load事件后。从缓存加载时,load事件不会触发。利用pageshow事件的persisted属性,可以判断页面是从何处加载而来:false标志第一次加载,true标志缓存加载。
- pagehide事件:通过“前进/后退”按钮,离开当前页面时触发。利用pagehide事件的persisted属性,设置为true,表示页面要保存在缓存中;反之,不保存。
- hashchange事件:URL的hash 部分(#号后面的部分,包括#号)发生变化时触发,在window对象上监听。
- popstate事件:只在浏览器的history对象的当前记录发生显式切换时触发。(比如鼠标点击“后退/前进”按钮)
d. 网页状态事件
- DOMContentLoaded事件
网页下载并解析完成以后,浏览器就会在document对象上触发,远早于load事件。
注:网页的JavaScript脚本是同步执行的,脚本一旦发生堵塞,将推迟触发DOMContentLoaded事件。
- readystatechange事件
当Document对象和XMLHttpRequest对象的readyState属性发生变化时触发。readyState有3种状态:
- loading:网页正在加载
- interactive:网页已经解析完成,但是外部资源仍然处在加载状态
- 和complete:网页和所有外部资源已经结束加载,load事件即将触发
e. 窗口事件
主要是scroll事件:在文档或文档元素滚动时触发(用户拖动滚动条)。
- 该事件会连续地大量触发,监听函数内不应有耗时操作;(resize事件亦是)
- 推荐使用requestAnimationFrame或setTimeout控制该事件的触发频率,结合customEvent抛出一个新事件;
目前lodash函数库提供了现成的throttle函数,可以直接使用:将一个函数的调用频率限制在一定阈值内
window.addEventListener('scroll', _.throttle(callback, 1000));
关于函数节流(throttle)和函数去抖(debounce)的问题,具体参见:函数节流与函数去抖 - sqh;
f. 剪贴板事件
cut
:将选中的内容从文档中移除,加入剪贴板时触发copy
:进行复制动作时触发paste
:剪贴板内容粘贴到文档后触发
这三个事件都是ClipboardEvent接口的实例。ClipboardEvent的实例属性clipboardData是一个DataTransfer对象,存放剪贴的数据。
关于事件类型的详细信息,参见:JS教程 - 事件种类;
扩展
(1)自定义渲染方法
即自定义jQuery插件,涉及:
- $.fn:定义方法
- $.extend(target, obj1, obj2, ...):定义参数
具体地,编写一个jQuery插件的原则:
- 给
$.fn
绑定函数,实现插件的代码逻辑 - 插件函数最后要
return this;
以支持链式调用 - 插件函数要有默认值,绑定在
$.fn.<pluginName>.defaults
上 - 用户在调用时可传入设定值以便覆盖默认值
$.fn.方法名 = function (options) { // 合并默认值和用户设定值: var opts = $.extend({}, $.fn.方法名.defaults, options); // 样式渲染 this.css('backgroundColor', opts.backgroundColor) .css('color', opts.color); // 返回this,支持链式调用 return this; } // 设定默认值: $.fn.方法名.defaults = { color: '#d85030', backgroundColor: '#fff8de' }
支持用户自定义默认值
$.fn.方法名.defaults.color = '#fff'; $.fn.方法名.defaults.backgroundColor = '#000';
也可以传参覆盖默认值。
(2)underscore库
完善的函数式编程接口,如同jQuery会绑定到$上,underscore会绑定到_上。
安装:npm install underscore
具体参见:underscore.js;