文档对象模型 (DOM) 是HTML和XML文档的编程接口。它给文档(结构树)提供了一个结构化的表述并且定义了一种方式—程序可以对结构树进行访问,以改变文档的结构,样式和内容。
DOM 提供了一种表述形式将文档作为一个结构化的节点组以及包含属性和方法的对象。从本质上说,它将web 页面和脚本或编程语言连接起来了。
要改变页面的某个东西,JavaScript就需要获得对HTML文档中所有元素进行访问的入口。这个入口,连同对 HTML 元素进行添加、移动、改变或移除的方法和属性,都是通过DOM来获得的
每个载入浏览器的HTML文档都会成为document对象。document对象包含了文档的基本信息,我们可以通过JavaScript对HTML页面中的所有元素进行访问、修改。
DOM2事件传播机制。
在所有的现代浏览器当中——除了IE9之前的版本——都实现了DOM2标准事件模型,这个事件模型规定:每一个DOM元素所触发的事件都要经历三个阶段:
- 事件捕获阶段:不太具体的节点更早接收事件,而最具体的元素最后接收事件
- 目标对象本身的事件处理程序调用阶段
- 事件冒泡阶段:事件开始时由最具体的元素接收,然后逐级向上传播到较为不具体的元素
在DOM2标准事件模型中,为一个DOM元素绑定事件的方法为addEventListener(),这个方法要求传递三个参数,第一个参数为一个字符串,表示事件的类型,如"click";第二个参数是一个函数,表示事件处理程序,浏览器会默认为该函数传递一个事件对象(Event);第三个参数是一个布尔值,布尔值为false表示此函数将注册为冒泡事件处理程序(通常设置为false即可),如果值为true,表示此函数将注册为捕获事件处理程序。值得注意的是,能通过多次调用addEventListener()为同一个对象注册同一事件类型的多个处理程序函数。当对象上发生事件时,所有该事件类型的注册处理程序都会按照注册的顺序调用。
分析事件的触发过程,分两种情况:
- 当目标元素不存在父元素或目标元素的父元素并没有注册与触发目标元素相同类型的事件时,事件模型的第一和第三阶段是没有实际意义的(即不会发生任何的事情)。
- 当目标元素存在父元素且目标元素的父元素注册了与触发目标元素相同类型的事件时,事件模型的第一和第三阶段就开始起作用了:
首先发生的是事件捕获,为截取事件提供机会,然后是实际目标接收事件,最后是冒泡阶段
事件传播机制Demo
事件传播过程.gif 阻止传播的效果.gif阻止传播
如果用户所点击的一个区域,在另一个区域或多个区域内,且每个区域都有相同的事件,那么触发了一个区域时,就会默认触发它的父区域的事件,这显然不是我们想要,这就可以通过阻止事件传播来实现
在支持addEventListener()
的浏览器中,可以调用事件对象的一个stopPropagation()
方法已阻止事件的继续传播。如果在同一对象上定义了其他处理程序,剩下的处理程序将依旧被调用,但调用 stopPropagation()
方法可以在事件传播期间的任何时间调用,它能工作在捕获阶段、事件目标本身中和冒泡阶段。
取消默认事件
可以通过调用事件对象的preventDefault()
方法取消事件的默认操作。
该方法将通知 Web 浏览器不要执行与事件关联的默认动作(如果存在这样的动作)。例如,如果 type 属性是 "submit",在事件传播的任意阶段可以调用任意的事件句柄,通过调用该方法,可以阻止提交表单。其它默认事件比如a链接默认跳转等等。
事件代理
事件代理用了事件冒泡的原理,把事件函数绑定到父元素上,在函数内指定子元素所需之行的方法。
当触发事件时,事件会依次冒泡到父元素上,然后又触发绑定在父元素上的函数。
也就是说指定一个事件处理程序,管理某一类型的所有事情。
我们来看一下事件代理的步骤:
- 父元素绑定事件
- 父元素知道事件的实际发生目标是谁
- 我们要对目标进行判断,如果是我们需要的元素,则发生回调函数
优点:
- 减少与dom的交互次数,提高代码性能
- 动态生成DOM元素时,可通过事件代理给新增的元素添加事件函数
列出DOM 元素选取的 API。
getElementById()
getElementById方法返回匹配指定ID属性的元素节点。如果没有发现匹配的节点,则返回null。这也是获取一个元素最快的方法
var elem = document.getElementById("test");
getElementsByClassName()
getElementsByClassName方法返回一个类似数组的对象(HTMLCollection类型的对象),包括了所有class名字符合指定条件的元素(搜索范围包括本身),元素的变化实时反映在返回结果中。这个方法不仅可以在document对象上调用,也可以在任何元素节点上调用。
var elements = document.getElementsByClassName('tab');
getElementsByClassName方法的参数,可以是多个空格分隔的class名字,返回同时具有这些节点的元素。
document.getElementsByClassName('red test');
getElementsByTagName()
getElementsByTagName方法返回所有指定标签的元素(搜索范围包括本身)。返回值是一个HTMLCollection对象,也就是说,搜索结果是一个动态集合,任何元素的变化都会实时反映在返回的集合中。这个方法不仅可以在document对象上调用,也可以在任何元素节点上调用。
var paras = document.getElementsByTagName("p");
上面代码返回当前文档的所有p元素节点。注意,getElementsByTagName方法会将参数转为小写后,再进行搜索。
getElementsByName()
getElementsByName方法用于选择拥有name属性的HTML元素,比如form、img、frame、embed和object,返回一个NodeList格式的对象,不会实时反映元素的变化。
// 假定有一个表单是<form name="x"></form>
var forms = document.getElementsByName("x");
forms[0].tagName // "FORM"
注意,在IE浏览器使用这个方法,会将没有name属性、但有同名id属性的元素也返回,所以name和id属性最好设为不一样的值。
querySelector()
querySelector方法返回匹配指定的CSS选择器的元素节点。如果有多个节点满足匹配条件,则返回第一个匹配的节点。如果没有发现匹配的节点,则返回null。
var el1 = document.querySelector(".myclass");
var el2 = document.querySelector('#myParent > [ng-click]');
querySelector方法无法选中CSS伪元素。
querySelectorAll()
querySelectorAll方法返回匹配指定的CSS选择器的所有节点,返回的是NodeList类型的对象。NodeList对象不是动态集合,所以元素节点的变化无法实时反映在返回结果中。
···
elementList = document.querySelectorAll(selectors);
···
querySelectorAll方法的参数,可以是逗号分隔的多个CSS选择器,返回所有匹配其中一个选择器的元素。
···
var matches = document.querySelectorAll("div.note, div.alert");
···
上面代码返回class属性是note或alert的div元素。
如何创建元素?如何添加元素?
创建元素
createElement()
createElement方法用来生成HTML元素节点。
var newDiv = document.createElement("div");
createElement方法的参数为元素的标签名,即元素节点的tagName属性。如果传入大写的标签名,会被转为小写。如果参数带有尖括号(即<和>)或者是null,会报错。
createTextNode()
createTextNode方法用来生成文本节点,参数为所要生成的文本节点的内容。
var newDiv = document.createElement("div");
var newContent = document.createTextNode("Hello");
上面代码新建一个div节点和一个文本节点
createDocumentFragment()
createDocumentFragment方法生成一个DocumentFragment对象。
var docFragment = document.createDocumentFragment();
DocumentFragment对象是一个存在于内存的DOM片段,但是不属于当前文档,常常用来生成较复杂的DOM结构,然后插入当前文档。这样做的好处在于,因为DocumentFragment不属于当前文档,对它的任何改动,都不会引发网页的重新渲染,比直接修改当前文档的DOM有更好的性能表现。
添加元素
appendChild()
在元素末尾添加元素
var newDiv = document.createElement("div");
var newContent = document.createTextNode("Hello");
newDiv.appendChild(newContent);
insertBefore()
在某个元素之前插入元素
var newDiv = document.createElement("div");
var newContent = document.createTextNode("Hello");
newDiv.insertBefore(newContent, newDiv.firstChild);
replaceChild()
replaceChild()接受两个参数:要插入的元素和要替换的元素
newDiv.replaceChild(newElement, oldElement);
onlick与addEventListener的区别?
onlick
若设置在HTML里面
优点:简单且具有跨浏览器优势。
缺点:
- 存在加载顺序问题,如果事件处理程序在html代码之后加载,用户可能在事件处理程序还未加载完成时就点击按钮之类的触发事件,存在时间差问题
- 这样书写html代码和JavaScript代码紧密耦合,维护不方便
若是设置在js里,如:btn.onclick = function(){}
优点:
- 删除事件处理程序,只需把元素的onclick属性赋为null。
- 简单且具有跨浏览器优势。
缺点:通常将一个方法赋值给一个元素的事件处理程序属性,也就意味着这个方法可以被新的方法覆盖,一个事件只能绑定一次。
addEventListener
可以有三个参数:
- 事件处理类型
- 事件处理方法
- 布尔类型,如果是true表示在捕获阶段调用事件处理程序,如果是false,则是在事件冒泡阶段处理
优点:能够绑定多个处理程序,会按照顺序依此执行
缺点:只能通过removeEventListener移除,移除时参数与添加的时候相同。不具备跨浏览器优势。
如何获取 DOM 计算后的样式?
使用getComputedStyle获取元素计算后的样式,不要通过 node.style.属性 获取
var node = document.querySelector('p')
var color = window.getComputedStyle(node).color
console.log(color)
写一个函数,批量操作 css。
function css(node, styleObj){
//todo ..
}
css(document.body, {
'color': 'red',
'background-color': '#ccc'
})
function setStyle(node, styleObj) {
for (var key in styleObj) {
node.style[key] = styleObj[key]
}
}
补全代码,要求:当鼠标放置在li元素上,会在img-preview里展示当前li元素的data-img对应的图片。
<ul class="ct">
<li data-img="1.png">鼠标放置查看图片1</li>
<li data-img="2.png">鼠标放置查看图片2</li>
<li data-img="3.png">鼠标放置查看图片3</li>
</ul>
<div class="img-preview"></div>
<script>
//你的代码
</script>
参考:
<ul class="ct">
<li data-img="http://cdn.jirengu.com/book.jirengu.com/img/13.jpg">鼠标放置查看图片1</li>
<li data-img="http://cdn.jirengu.com/book.jirengu.com/img/14.jpg">鼠标放置查看图片2</li>
<li data-img="http://cdn.jirengu.com/book.jirengu.com/img/15.jpg">鼠标放置查看图片3</li>
</ul>
<div class="img-preview"></div>
<script>
function $(selector) {
return document.querySelector(selector);
}//定义一个函数$(),功能等同于document.querySelector()
$('.ct').addEventListener('mouseover',function(showImg){
if (showImg.target.tagName.toLowerCase() === 'li'){
var imgLink = showImg.target.getAttribute("data-img"); //获取图片链接
$('.img-preview').innerHTML = '<img src="'+imgLink+'">';//在制定位置插入图片链接
}
});
</script>
<ul class="ct">
<li data-img="http://cdn.jirengu.com/book.jirengu.com/img/11.jpg">鼠标放置查看图片1</li>
<li data-img="http://cdn.jirengu.com/book.jirengu.com/img/13.jpg">鼠标放置查看图片2</li>
<li data-img="http://cdn.jirengu.com/book.jirengu.com/img/14.jpg">鼠标放置查看图片3</li> </ul>
<div class="img-preview"></div>
<script>
var dar1 = document.querySelector('.ct')
var dar2 = document.querySelector('.img-preview')
dar1.addEventListener('mouseover',function(e){
var img = e.target.getAttribute('data-img')
dar2.innerHTML = "<img src="+img+">"
}
)
</script>
补全代码,要求:
当点击按钮开头添加时在<li>这里是</li>元素前添加一个新元素,内容为用户输入的非空字符串;当点击结尾添加时在最后一个 li 元素后添加用户输入的非空字符串.
当点击每一个元素li时控制台展示该元素的文本内容。
<ul class="ct">
<li>这里是</li>
<li>饥人谷</li>
<li>任务班</li>
</ul>
<input class="ipt-add-content" placeholder="添加内容"/>
<button id="btn-add-start">开头添加</button>
<button id="btn-add-end">结尾添加</button>
<script>
//你的代码
</script>
参考:
<ul class="ct">
<li>这里是</li>
<li>饥人谷</li>
<li>任务班</li>
</ul>
<input class="ipt-add-content" placeholder="添加内容"/>
<button id="btn-add-start">开头添加</button>
<button id="btn-add-end">结尾添加</button>
<script>
function $(selector){
return document.querySelector(selector);
} //定义一个函数$(),功能等同于document.querySelector()
$('.ct').addEventListener("click",function(e){
if(e.target.tagName.toLowerCase() === 'li'){
console.log(e.target.innerText);
}
});//添加点击事件
$('#btn-add-start').onclick = function(){
if($('.ipt-add-content').value !== ""){ //先判断输入框内是否为空
var newli = document.createElement('li'); //创建一个新的标签<li></li>
newli.innerText = $('.ipt-add-content').value;//输入框内容传递给新标签的文本内容
$('.ct').prepend(newli); //在<ul>内部的开头插入新标签<li>文本<li>
}
};
$('#btn-add-end').onclick = function(){
if($('.ipt-add-content').value !== ""){
var newli = document.createElement('li');
newli.innerText = $('.ipt-add-content').value;
$('.ct').append(newli);
}
};
</script>
有如下代码,要求当点击每一个元素li时控制台展示该元素的文本内容。不考虑兼容。
<ul class="ct">
<li>这里是</li>
<li>地球</li>
<li>中国</li>
</ul>
<script>
//todo ...
</script>
<ul class="ct">
<li>这里是</li>
<li>地球</li>
<li>中国</li>
</ul>
<script>
function $(selector){
return document.querySelector(selector);
}
$('.ct').addEventListener('click', function(e){
if(e.target.tagName.toLowerCase() === 'li'){
console.log(e.target.innerText);
}
})
</script>
网友评论