Fork me on GitHub

红宝书的笔记 - DOM和事件

Text类型

nodeType值为3,nodeName为#text,nodeValue是包含的文本。

  • doucment.createTextNode(str):创建新的文本节点

    1
    2
    3
    4
    5
    6
    7
    8
    9
    var ele = document.createElement("div");
    var textnode = document.createTextNode("hello");
    ele.appendChild(textnode);

    //或者再添加一个文本节点(同胞节点)
    var textnode2 = document.createTextNode(" world");
    ele.appendChild(textnode2);

    document.body.appendChild(ele);
  • normalize():合并目标下所有文本节点

    1
    2
    3
    4
    5
    6
    //在上一段的基础上
    console.log(ele.childNode.length); //2

    ele.normalize();
    console.log(ele.childNode.length); //1
    console.log(ele.firstNode.nodeValue); //hello world
  • splitText(index):分割文本节点,按照指定位置分割成两个文本节点

Comment类型

nodeType值为8,nodeName为#comment,nodeValue是注释的内容。

1
2
var comment = document.createComment("a comment");
//创建一个<!-- a comment-->

Attr类型

nodeType值为2,nodeName为特性的名称,nodeValue是特性的值。

1
2
3
4
5
6
7
8
var attr = document.createAttribute('align');
attr.value = 'left';
element.setAttributeNode(attr);

console.log(element.attributes["align"].value);
console.log(element.getAttributeNode["align"].value);
console.log(element.getAttribute["align"]);
//left

动态脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//动态引入外部脚本
function loadScript(url){
var script = document.createElement("script");
script.type = 'text/javascript';
script.src = url;
document.body.appendChild(script);
}
//动态加载行内脚本
var str = "function say(){alert('hi');}";
function addScript(str){
script = document.createElement("script");
script.type = 'text/javascript';
//script.text = str;

//兼容ie和safari
try{
script.appendChild(document.createTextNode(str));
}catch (ex){
script.text = str;
}
document.body.appendChild(script);
}

动态样式

和动态脚本类似。

1
2
3
4
5
6
7
8
//引入外部style
function loadStyle(url){
var link = document.createElement("link");
link.type = 'text/css';
link.rel = 'stylesheet';
link.href = url;
document.getElementsByTagName('head')[0].appendChild(link);
}

操作表格

DOM扩展

querySelector()方法

接收一个css选择符,返回匹配的第一个元素或者 null。

1
2
var mydiv = document.querySelector('#mydiv');
var img = mydiv.querySelector('img.button');

querySelectorAll()方法

返回一个nodelist的实例。和querySelector类似。

matchesSelector()方法

检测调用元素与该选择符是否匹配。(部分浏览器不支持)

元素遍历

  • childElementCount:返回子元素的个数
  • firstElementChild和lastElementChild:指向第一个子元素/最后一个子元素
  • previousElementSibling和nextElementSibling:指向前一个兄弟元素/后一个兄弟元素

html5相关

  1. getElementsByClassName()

接收一个或多个类名的字符串,返回nodelist。

1
2
var ele = document.getElementsByClassName('username current');
var selected = document.getElementById('mydiv').getElementsByClassName('selected');

  1. classList属性

    1
    2
    3
    4
    5
    6
    div.classList.remove('classA').add('classB').toggle('classC');
    //div移除类classA 添加classB 切换classC
    //判断元素是否包含类名
    if(div.classList.contains('classA') && !div.classList.contains('classB')){
    //dosomething
    }
  2. 焦点管理

document.activeElement 属性:引用DOM中当前聚焦元素。默认情况下文档加载完时保存的是document.body,文档加载期间是null

document.hasFocus():判断是否聚焦
4. HTMLdocument扩展

document.readyState:loading正在加载文档;complete加载完成

document.compatMode:CSS1Compat标准模式;BackCompat混杂模式

document.charset和document.defaultCharset:文档使用的字符集
5. 插入标记

window.toStticHTML():传入html字符串,返回删除所有脚本节点和事件处理属性的html字符串

outerHTML():返回指定元素及其子元素的html标签;或替换指定元素及其子元素

innerHTML():返回指定节点的子元素;或替换子元素

insertAdiacentHTML():参数传插入位置(beforebegin/afterbegin/beforeend/afterend)和插入的html文本。
6. 其他扩展

parentNode.contains(node):检测该节点是否是后代节点

innerText():返回指定元素文档树中的文本;或插入文本替换指定元素的所有子节点

scrollIntoView():滚动

样式

访问元素的样式

1
2
var div = document.getElementById('mydiv');
div.style.backgroundColor = 'green';

DOM样式属性和方法:

1
2
3
4
5
6
7
8
9
//设置
div.style.cssText = "width:100px;height:100px;background:blue;";
//循环
for(var i = 0, len = div.style.length; i < len; i++){
var prop = div.style[i];//属性名
var value = div.style.getPropertyValue(prop);//属性值
}
//删除
div.style.removePropery('width');

计算属性(只读):

1
2
3
4
5
6
7
8
//获取完整的属性(包括<style>样式表中的属性)
var computeStyle = document.defaultView.getComputedStyle(div,null);
//第二个参数可以传伪元素字符串(":after")
console.log(computeStyle.border);

//ie中不支持getComputedStyle()
var computeStyle = div.currentStyle;//ie
console.log(computeStyle.border);

操作样式表

css规则:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//获取样式表中的css
var sheet = document.styleSheet[0];
var rules =sheet.cssRules || sheet.rules;
var rule = yules[0]; //获取第一条规则
console.log(rule.style.cssText);
console.log(rule.style.width);

rule.style.backgroundColor = 'yellow';

//插入
sheet.insertRule("body{background:#f6f6f6}",0);//插入规则,插入位置
sheet.addRule("body","background:#f6f6f6",0);//ie

//删除
sheet.deleteRule(0);
sheet.removeRule(0);//ie

元素大小

偏移量

  • offsetHeight:元素在垂直方向占用的空间(border+padding+content)
  • offsetWidth:元素在水平方向占用的空间
  • offsetLeft:元素的左外边框至父元素的左内边框的距离
  • offsetTop:元素的上外边框至父元素的上内边框的距离

客户区大小:

clientHeight = height + paddingTop + paddingBottom

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//浏览器的视口大小
function getViewport(){
if(document.compatMode == 'BackCompat'){
return {
width:document.body.clientWidth,
height:document.body.clientHeight
}
}else{
return {
width:document.documentElement.clientWidth,
height:document.documentElement.clientHeight
}
}
}

滚动大小:

  • scrollHeight:在没有滚动条时的元素内容总高度
  • scrollWidth:在没有滚动条时的元素内容总宽度
  • scrollTop:滚动高度
  • scrollLeft:横向滚动距离

遍历

  1. NodeIterator

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    var filter = function(node){
    return node.tagname.toLowerCase() == 'p' ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP;
    };
    var iterator = document.createNodeIterator(root,NodeFilter.SHOW_ELEMENT,filter,false);
    //创建实例(遍历的根节点,显示的节点类型,过滤器,是否扩展实体引用)

    var node = iterator.nextNode(); //指向下一个节点
    //var node = iterator.previousNode();
    while (node != null){
    console.log(node.tagName); //输出标签名
    node = iterator.nextNode();
    }
  2. TreeWalker

document.createTreeWalker()和document.createNodeIterator方法类似,因此TreeWalker可以代替NodeIterator。TreeWalker可以在DOM结构中沿任何方向移动。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var div = documnet.getElementById('div');
var waiker = document.createTreeWalker(div,NodeFilter.SHOW_ELEMENT,null,false);

walker.firstChild(); //当前节点的第一个子节点
walker.nextSibling(); //下一个兄弟节点
//walker.previousSibling(); //上一个兄弟节点
//walker.parentNode(); //当前节点的父节点

var node = walker.firstChild();
while (node !== null){
console.log(node.tagName);
console.log(node === walker.currentNode);
//currentNode表示上一次遍历中返回的节点,通过这个属性可以修改下次遍历的起点
node = walker.nextSibling();
}

范围

简单选择:

1
2
3
4
5
6
var range1 = document.createRange(),
range2 = document.createRange();
var div = document.getElementById();

range1.selectNode(div); //选择整个节点及其子节点
range2.selectNodeContents(div); //选择该节点下的子节点

复杂选择:

借助setStart()和setEnd()可以选择节点的一部分。

1
2
3
4
5
6
7
var p = document.getElementById('p');
var hello = p.firstChild.firstChild,
world = p.lastChild;
var range = document.createRange();

range.setStart(hello,2);
range.setEnd(world,3);//选择范围

操作选区:

1
2
3
4
//接上
range.deleteContent();//删除选区
var fragment = range.extractContents(); //删除选区,返回范围的文档片段
var clonement = range.cloneContents(); //创建范围的一个副本

事件

事件是用户或浏览器自身执行的某种动作。

事件冒泡

事件开始时由最具体的元素接收然后逐级向上传播。

事件捕获

事件捕获是不具体的节点更早接收事件,然后往下传播到最具体的点。

事件处理程序

响应某个事件的函数。

  1. DOM0级事件处理程序

    1
    2
    3
    4
    5
    6
    7
    var btn = document.getElementById("mybtn");
    btn.onclick = function(){//事件处理程序在所属元素的作用域内运行
    console.log(this.id);
    }

    //删除
    btn.onclick = null;
  2. DOM2级事件处理程序

addEventListener():传递3个参数,要处理的事件名,函数,true(事件捕获)/false(事件冒泡)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var btn = document.getElementById("mybtn");
btn.addEventListener("click",function(){
console.log(this.id);
},false);

btn.addEventListener("click",function(){
console.log("click!");
},false);//事件按顺序触发

var func = function(){
alert("abc");
}
btn.addEventListener("click",func,false);//匿名函数才能移除
btn.removeEventListener("click",func,false);//移除事件

  1. IE事件处理程序
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    var btn = document.getElementById("mybtn");
    btn.attachEvent("onclick",function(){
    //事件处理程序在全局作用域中运行
    console.log(this === window); //true
    },false);

    btn.attachEvent("onclick",function(){
    console.log("click2"); //true
    },false);//按照相反的顺序执行

    var func = function(){
    alert("abc");
    }
    btn.attachEvent("onclick",func);//匿名函数才能移除
    btn.detachEvent("onclick",func);//移除事件

事件对象

DOM中的事件对象
1
2
3
4
var btn = document.getElementById("mybtn");
btn.addEventListener("click",function(event){
console.log(event.type);
},false);

在事件处理程序内部,对象this等于event.currentTarget。而event.target只包含事件的实际目标。

1
2
3
4
5
var btn = document.getElementById("mybtn");
btn.onclick = function(event){//直接将事件处理程序指定给目标程序
console.log(event.currentTarget === this);//true
console.log(event.target === this);//true
};

处理多个事件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var btn = document.getElementById("mybtn");
var handler = function(event){
switch(event.type){
case "click":
console.log("click");
break;
case "mouseover":
event.target.style.color = 'red';
break;
case "mouseout":
event.target.style.color = '';
break;
}
}
btn.onclick = handler;
btn.onmouseover = handler;
btn.onmouseout = handler;

其他:

1
2
3
4
5
6
7
8
9
10
11
12
var btn = document.getElementById("mybtn");
btn.onclick = function(event){
event.preventDefault();//阻止特定事件的默认行为

event.stopPropagation();//阻止事件冒泡或捕获

console.log(event.eventPhase);
//确定事件处于事件流的哪个阶段
//1 在捕获阶段调用
//2 事件处理程序处于目标上
//3 在冒泡阶段调用
};
IE的事件对象

事件处理程序的作用域是根据指定他的方式来确定的。

1
2
3
4
5
6
7
8
9
10
11
12
var btn = document.getElementById("button");
btn.onclick = function(){
console.log(window.event.srcElement === this); //true

window.event.cancelBubble = true;
//ie不支持事件捕获 因此只能阻止事件冒泡

window.event.returnValue = false; //阻止事件默认行为
}
btn.attachEvent("onclick",function(e){
console.log(e.srcElement === this); //false
})
跨浏览器事件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
var EventUtil = {
addHander:function(elem,type,handler){
},
getEvent:funtion(event){
return event? event:window.event;
},
getTarget:function(event){
return event.target|| event.srcElement;
},
preventDefault:function(event){
if(event.preventDefault){
event.preventDefault();
}else{//ie
event.returnValue = false;
}
},
removeHandler:function(elem,type,handler){

},
stopPropagation:function(event){
if(event.stopPropagation){
event.stopPropagation();
}else{
event.cancelBubble = true;
}
},
getRelatedTarget:function(event){
//只有mouseout 和mouseover有相关元素
if(event.relatedTarget){
return event.relatedTarget;
}else if(event.toElement){
return event.toElement;
}else if(event.fromElement){
return event.fromElement;
}eles{
return null;
}
},
}

事件类型

1
2
//确定浏览器是否支持DOM3级事件
var supprot = document.implementation.hasFeature("UIEvent","3.0");
  1. load : 页面完全加载后(包括外部资源加载完成后)触发
  2. unload : 文档被完全卸载后触发(从一个页面切换到另一个页面)。
  3. resize : 当窗口或者框架大小变化时触发
  4. scroll : 窗口或元素滚动时触发
  5. abort : 停止下载时,嵌入的内容没有加载完在object上触发
  6. error : 发生错误时
  7. select : 选择文本框中的字符时
  8. blur/focusout : 失焦(不冒泡/冒泡)
  9. focus/focusin : 聚焦
  10. click : 单击(左键)或按下回车
  11. dblclick : 双击(左键)
  12. mousedown : 按下任意鼠标
  13. mouseenter : 首次移动到元素范围内触发
  14. mouseleave : 移到元素范围外
  15. mousemove : 在元素内部移动时重复触发
  16. mouseout : 移到另一个元素
  17. mouseover : 鼠标在一个元素外部,首次移入另一个元素
  18. mouseup : 释放鼠标按钮触发
  19. mousewheel : 鼠标滚轮事件
  20. keydown : 按键盘任意键触发,按住不放会重复触发
  21. keypress : 按键盘字符键触发,按住不放会重复触发
  22. keyup : 释放按键触发
  23. textInput : 文本框输入
事件位置
  • event.clientX/Y : 鼠标事件在客户端坐标信息
  • pagaX/Y : 在页面中的坐标
  • screenX/Y : 相对浏览器的位置
HTML5事件
  1. contextmenu : 自定义显示上下文菜单(如点击鼠标右键)
1
2
3
4
5
6
7
8
9
10
11
12
13
var div = document.getElementById("mydiv");
EventUtil.addHandler(div,"contextmenu",function(event){
event = EventUtil.getEvent(event);
EventUtil.preventDefault(event);

var menu = document.getElementById("mymenu");
menu.style.left = event.clientX+"px";
menu.style.top = event.clientY+"px";
menu.style.visibility = "visible";
})
EventUtil.addHander(document,"click",function(){
document.getElementById("mymenu").style.visibility = "hidden";
})
  1. beforeunload : 页面卸载前触发

    1
    2
    3
    4
    5
    6
    EventUtil.addHandler(window,"beforeunload",function(event){
    event = EventUtil.getEvent(event);
    var msg = "确定关闭?";
    event.returnValue = msg;
    return msg;
    })
  2. DOMContentLoaded : 页面load时形成完整的DOM树就触发,在load前触发

  3. readystatechange :提供文档或元素有关的加载信息

  4. pageshow和pagehide : (往返缓存有关,有些浏览器回退页面时不触发load)在页面显示时触发,一般在load之后;pagehide是在卸载页面时触发,在unload前触发;event保存属性persisted判断页面是否是回退的(在bfcache中)

  5. haschange : 在url参数变化时触发

    1
    2
    3
    EventUtil.addHandler(window,"beforeunload",function(event){
    console.log("current hash:"+location.hash)
    })
设备事件
  • orientationchange : (苹果safari)设备切换(横纵)方向时触发;window.orientation属性中保存当前旋转角度
  • MozOrientation : (firefox)设备方向改变
  • deviceorientation : 设备方向改变
  • devicemotion : 包含设备的运动信息
触摸事件
  • touchstart : 当手指触摸屏幕时
  • touchmove : 手指在屏幕上滑动时连续触发。期间调用preventDefault()可以阻止滚动
  • touchend : 手指从屏幕移开
  • touchcancel : 当系统停止跟踪触摸

以上事件都是冒泡的,也都是可以取消的。包含用于跟踪触摸的属性:

  • touches : 表示当前跟踪触摸的对象数组
  • targetTouchs : 特定于事件目标的Touch对象的数组
  • changeTouches : 表示从上次触摸以来改变了的对象的数组

顺序:touchstart - mouseover - mousemove - mousedown - mouseup - click - touchend

手势事件:只有两个手指都触摸时才会触发

  • getsturestart : 当一个手指按在屏幕而另一个手指又触摸屏幕
  • getsturechange : 当触摸屏幕的手指位置变化
  • getstureend : 当手指移开屏幕

内存和性能

事件委托

在js中,添加到页面上的事件处理程序数量将直接关系到页面的整体运行。因此需要事件委托,事件委托利用了事件冒泡,只指定一个事件处理程序管理某一类型的事件。

1
2
3
4
5
6
7
8
9
10
11
12
13
var list = doucment.getElementById("links");
EventUtil.addHandler(list,"click",function(e){
e = EventUtil.getEvent(e);
var target = EventUtil.getTarget(e);
switch(target.id){
case "li1":
document.title = 'aaa';
break;
case "li2":
location.herf = 'index.html';
break;
}
})

PS: 当事件处理程序指定给元素时,浏览器代码和脚本就会建立连接,这种连接越多页面越慢。除了利用事件委托还可以移除无用的事件处理程序来提升性能。

1
2
3
4
btn.onclick = function(){
btn.onclick = null; //移除事件处理程序
doucment.getElementById("btnParent").innerHTML = "Processing...";
}
模拟事件

可以在任何时候触发模拟事件,可用来测试web程序。模拟事件也可以冒泡。

1
2
3
4
5
6
7
8
9
//模拟鼠标事件

var btn = document.getElementById("btn");
//创建事件对象
var event = doucment.createEvent("MouseEvents");
event.initMouseEvent("click",true,true,doucment.defaultView,0,0,0,0,0,false,false,false,false,0,null);

//触发事件
btn.dispatchEvent(event);

表单脚本

表单

1
2
3
4
//获取表单
var forms = document.forms;
var firstForm = forms[0];
var myForm = forms["form2"];//name = form2
  1. 提交表单

表单中包含button/input,type=”submit”或者type=”image”(图片按钮),点击或enter可提交表单。

form.submit()提交表单时不会触发submit事件,所以在此之前要先验证表单数据。
2. 重置表单

input/button,type=”reset”可以重置表单。form.reset()会触发reset事件。
3. 表单字段

1
2
3
4
5
6
var form = document.getElementById("form1");

//表单的第一个字段
var field = form.elements[0];
//获取表单中name="field2"的字段
var field2 = form.elements["field2"];

表单字段属性:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var form = document.getElementById("myform");
var field = form.elements[0];

//修改value属性
field.value = "aaa";

//检查form属性
console.log(field.form == form); // true

//设置焦点
field.focus();

//禁用
field.disabled = true;

//修改type属性(input)
feild.type = "text";

-------------完结撒花 -------------

本文标题:红宝书的笔记 - DOM和事件

文章作者:咕噜咕噜

发布时间:2019年01月21日

最后更新:2022年08月31日

原始链接:https://aartemida.github.io/2019/01/21/红宝书的笔记2/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。