Dom、事件

一、问答

1. dom对象的innerTextinnerHTML有什么区别?

  • innerHTML: 也就是从对象的起始位置到终止位置的全部内容,包括Html标签。
  • innerHTML: 从起始位置到终止位置的内容, 但它去除Html标签 。
测试

2. elem.childrenelem.childNodes的区别?

  • elem.children:它返回指定元素的子元素集合, 不会返回TextNode。用的比较多
    -elem.childNodes: 它返回指定元素的子元素集合,包括HTML节点,所有属性,文本。
测试

3. 查询元素有几种常见的方法?

  • 原生JS获取元素的方法
    (1).通过顶层document节点获取:
    • document.getElementById(elementId):该方法通过节点的ID,可以准确获得需要的元素,是比较简单快捷的方法。如果页面上含有多个相同id的节点,那么只返回第一个节点。
    • document.getElementsByName(elementName):用来选择一些具有name属性的html元素,比如form、img等,返回一个储存对应类名的类数组对象,要用arr[i] 来使用,只有 document
      能使用
    • document.getElementsByTagName(tagName):该方法是通过节点的Tag获取节点,同样该方法也是返回一个数组
    • document.getElementsByClassName(className):通过类名来选择元素,返回一个储存对应类名的类数组对象
    • querySelector():使用css选择器语法来选中目标元素,会返回匹配到的元素节点如果有多个满足条件的元素节点将返回第一个
    • querySelectorAll() :与querrSelector类似,返回所有满足条件的元素节点,是一个类数组对象

4. 如何创建一个元素?如何给元素设置属性?

1.如何创建一个元素

  • document.createElement(): 用来生成HTML元素节点;
    -createTextNode():createTextNode方法用来生成文本节点,参数为所要生成的文本节点的内容。
    -createDocumentFragment():createDocumentFragment方法生成一个DocumentFragment对象。
<!doctype html>
<html>
<head>
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
<div id="container">
    <ul id="bar">
        <li class="item">你</li>
        <li class="item">是</li>
        <li class="item">谁</li>
        <li class="item">在</li>
        <li class="item">干</li>
    </ul>
</div>
<script>
    function $$(id){
        return document.getElementById(id);
    }
    function tag(className){
        return document.getElementsByClassName(className);
    }
</script>
</body>
</html>
测试

2.如何给元素设置属性

  • HTML元素对象有一个attributes属性,返回一个类似数组的动态对象,成员是该元素标签的所有属性节点对象,属性的实时变化都会反映在这个节点对象上。其他类型的节点对象,虽然也有attributes属性,但是返回的都是null,因此可以把这个属性视为元素对象独有的。
  • 常用有两种方法:第一种是直接用HTML标签对象进行读写操作,第二种是用setAttribute()方法进行读写;
测试

5. 元素的添加、删除?

  • 添加元素的方法:
    • 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);```

  • 删除元素的方法:
    • removeChild()

parentNode.removeChild(childNode);


![节点对象的方法](http://upload-images.jianshu.io/upload_images/2775927-59814a76c7241b25.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)


###6. DOM0 事件和DOM2级在事件监听使用方式上有什么区别?
- 概念:事件是某些动作发生时产生的信号。JavaScript能够感知这些信号,因此能够编写代码来响应这些信号,也就是响应这些事件。而响应某个事件的函数就叫做**事件处理程序(或事件侦听器)**。事件处理程序的名字以on开头,所以click点击事件的事件处理程序就是onclick,load事件的事件处理程序就是onload等等。

- DOM0级事件处理程序:
因为在HTML代码中直接指定事件处理程序存在一些缺陷,所以人们转而使用JavaScript来指定事件处理程序。通过JavaScript来指定事件处理程序的方式就是将一个函数赋值给一个事件处理属性。
在DOM0级事件处理程序中,要使用JavaScript来指定事件处理程序,首先需要获取操作对象的引用。每一个元素对象都有自己的事件处理属性,例如onclick
,将这些属性的值设置为一个函数,就可以指定事件处理程序了。例如:

<input type="button" value="点击" id="btn">
<script type="text/javascript">
var btn = document.getElementById("btn");
btn.onclick = function(){
alert("你点击了按钮");
}
</script>

使用DOM0级方法指定的事件处理程序被认为是元素的方法。这时候的事件处理程序是在元素的作用域中运行的,也就是说,在事件处理程序中,this
引用就是这个元素对象。例如:

<input type="button" value="点击" id="btn">
<script type="text/javascript">
var btn = document.getElementById("btn");
btn.onclick = function(){
alert(this.id); //输出“btn”
}
</script>

我们可以在上事件处理程序中使用this
关键字来获取元素的所有属性和方法。以这种方式添加的事件处理程序会在[事件流](http://www.htmleaf.com/ziliaoku/qianduanjiaocheng/201512012851.html)的冒泡阶段被处理。例如使用下面的代码可以查看事件冒泡的情况。

<html lang="zh" id="html" class="demo">
<head>
<meta charset="UTF-8">
<title>事件冒泡</title>
</head>
<body id="body" class="demo">
<div id="parent" class="demo">
<button id="child" class="demo">点击一下</button>
</div>
</body>
</html>

下面是控制台中输出的结果:

![firefox-event-bubbling.jpg](http://upload-images.jianshu.io/upload_images/2775927-64a7f8e287ba6de5.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

从控制台中的输出结果可以看出,由上到下的顺序为child→parent→body→html,说明事件最先发生在按钮(child)上面,然后逐级往上冒泡。
DOM0事件处理程序会产生事件冒泡。如果要取消事件冒泡,可以使用event.stopPropagation()方法,但是IE浏览器不支持这个方法,在IE中,取消事件冒泡的方法为:event.cancleBubble = true。

//取消事件冒泡
event.stopPropagation(); //DOM方法
event.cancleBubble = true; //IE的方法

另外要提的一点是DOM0事件处理程序不支持事件捕获。
我们也可以删除通过DOM0级方法指定的事件处理程序,只要将事件处理程序属性的值设置为null即可。

btn.onclick = null;

使用DOM0级事件处理程序最大的问题是事件会被覆盖。来看下面的例子:

<input type="button" value="点击" id="btn">
<script type="text/javascript">
var btn = document.getElementById("btn");
btn.onclick = function(){
console.info("event-one");
}
btn.onclick = function(){
console.info("event-two");
}
btn.onclick = function(){
console.info("event-three");
}
</script>


在上面的代码中,通过按钮的引用对象为按钮添加了3个事件处理函数,那么现在的问题是哪一个事件处理程序会被执行?答案是第三个事件处理程序会被执行,控制台中会输出“event-three”。这是因为btn.onclick
实际上就像一个指针,在执行第一个事件处理程序时它指向了内存中的一个引用,执行第二个事件处理程序时它又会指向另一个内存的引用,最终会指向第3个事件处理函数的内存引用。

-  DOM2级事件处理程序

由于DOM0级事件处理程序会产生事件冒泡和事件覆盖等问题,W3C组织制定了“DOM2级事件处理程序”。“DOM2级事件处理程序”定义了2个方法,用于处理指定和删除事件处理程序的操作,这2个方法是:```addEventListener```和```removeEventListener```。所有的DOM节点中都包含这两个方法,这些方法接收3个参数:**要处理的事件名称**、**作为事件处理程序的函数**和**一个布尔值**。布尔值参数如果设置为**true**,表示在**捕获阶段调用事件处理程序**,如果设置为**false**,表示在事件**冒泡阶段调用事件处理程序**。

如果要在一个按钮上添加DOM2级事件处理程序,可以使用下面的代码:

<input type="button" value="点击" id="btn">
<script type="text/javascript">
var btn = document.getElementById("btn");
btn.addEventListener("click",function(){
alert(this.id);
},false);
</script>

上面的代码为按钮添加了一个DOM2级事件处理程序,事件名称是click,对应DOM0级的onclick属性。因为最后一个参数为false,所以这个事件会在冒泡阶段被触发。

和DOM0级事件处理程序相同,DOM2级事件处理程序也会依附在元素的作用域中运行。

使用DOM2级事件处理程序就可以避免DOM0级事件处理程序的事件覆盖问题。例如下面的例子:

<input type="button" value="点击" id="btn">
<script type="text/javascript">
var btn = document.getElementById("btn");
btn.addEventListener("click",function(){
console.info("event-one");
},false);
btn.addEventListener("click",function(){
console.info("event-two");
},false);
btn.addEventListener("click",function(){
console.info("event-three");
},false);
</script>

查看控制台我们会发现,3个事件都会被执行,控制台中依次打印出:“event-one”、“event-two”和“event-three”。

接下来,我们可以来看一下事件捕获的情况。[上一篇文章](http://www.htmleaf.com/ziliaoku/qianduanjiaocheng/201512012851.html)只是介绍了事件捕获,并没有举一个具体的例子。下面我们在DOM2级事件处理程序中来查看事件捕获的过程。代码如下:

<html lang="zh" id="html" class="demo">
<head>
<meta charset="UTF-8">
<title>事件捕获</title>
</head>
<body id="body" class="demo">
<div id="parent" class="demo">
<button id="child" class="demo">点击一下</button>
</div>
<script type="text/javascript">
window.onload = function(){
var elements = document.querySelectorAll(".demo");
for(var i = 0 ; i < elements.length; i++){
elements[i].addEventListener("click",function(event){
var element = elements[i];
//获取事件
event = event?event:window.event;
//获取事件发生的目标
target = event.target?event.target:event.srcElement;
console.info("事件源:"+target.id+" , "+target.tagName+", 事件对象:"+this.id);
},true);
}
}
</script>
</body>
</html>


由于在上面的事件处理程序中,第3个参数被设置为true
,表示在捕获阶段调用事件处理程序。在Firefox浏览器的控制台中得到下面的结果:


![firefox-dom2-event.jpg](http://upload-images.jianshu.io/upload_images/2775927-44e32d854f93b284.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

从控制台的结果中可以看到,事件源是按钮,事件对象从上往下分别为:
<document>→<body>→<parent>→<child>。

通过addEventListener()方法添加的事件处理程序只能通过removeEventListener()方法来移除。移除时传入的参数和添加事件处理程序时的参数一样。注意:通过addEventListener()方法添加的匿名函数将无法移除。下面是一个移除DOM2级事件处理程序的例子:

var btn = document.getElementById("btn");
btn.addEventListener("click",function(){
console.info(this.id);
},false);
......
btn.removeEventListener("click",function(){/函数在此处没有用/},false);


传入removeEventListener()中的事件处理函数必须与addEventListener()中的事件处理函数相同,例如:

var fn = function(){
console.info(this.id);
}
var btn = document.getElementById("btn");
btn.addEventListener("click",fn,false);
......
btn.removeEventListener("click",fn,false);

在多数情况下,都是将事件处理程序添加到事件流的事件冒泡阶段,这样可以最大限度的兼容各种浏览器。最好只在事件到达目标之前截获它的时候将事件处理程序添加到事件捕获阶段。

###7. ```attachEvent```与```addEventListener```的区别?

- 在添加事件处理程序事addEventListener和attachEvent主要有几个区别:

  (1). **参数个数不相同**,这个最直观,addEventListener有三个参数,attachEvent只有两个,attachEvent添加的事件处理程序只能发生在冒泡阶段,addEventListener第三个参数可以决定添加的事件处理程序是在捕获阶段还是冒泡阶段处理(我们一般为了浏览器兼容性都设置为冒泡阶段)

  (2). **第一个参数意义不同**,addEventListener第一个参数是事件类型(比如click,load),而attachEvent第一个参数指明的是事件处理函数名称(onclick,onload)

  (3). **事件处理程序的作用域不相同**,addEventListener的作用域是元素本身,this是指的触发元素,而attachEvent事件处理程序会在全局变量内运行,this是window,所以刚才例子才会返回undefined,而不是元素id

  (4). **为一个事件添加多个事件处理程序时,执行顺序不同**,addEventListener添加会按照添加顺序执行,而attachEvent添加多个事件处理程序时顺序无规律(添加的方法少的时候大多是按添加顺序的反顺序执行的,但是添加的多了就无规律了),所以添加多个的时候,不依赖执行顺序的还好,若是依赖于函数执行顺序,最好自己处理,不要指望浏览器


###8. 解释IE事件冒泡和DOM2事件传播机制?

-  事件冒泡:
IE的事件流叫做事件冒泡(event bubbling),它是指事件最初由最具体的元素接收,然后逐级向上传播到较为不具体的节点。例如有下面的HTML代码:

<html lang="zh">
<head>
<meta charset="UTF-8">
<title>JavaScript事件</title>
</head>
<body>
<button id="btn">点击一下</button>
</body>
</html>

如果你点击了页面中的按钮,那么事件将按照如下的顺序进行传播:
<button>→<body>→<html>→<document>
也就是说,点击事件实际发生在<button>元素上,然后事件会沿着DOM树向上传播,在每一个DOM节点上都会发生,一直传播到document对象。
所有的现代浏览器都支持事件冒泡,但是在具体实现上略有差别。

- 事件捕获:
事件捕获的思想是不具体的节点应该更早的接收到事件,最具体的节点最后才接收到事件。事件捕获的用意在于在事件到达预定的目标之前捕获它。还是以上面的HTML页面的代码为例子,在点击了按钮之后,事件的传播方式为:
<document>→<html>→<body>→<button>
在事件捕获的过程中,document对象首先接收到点击事件,然后事件会沿着DOM树依次向下,一直传播到事件的目标元素。
Firefox、Chrome、Safari和Opera浏览器都支持事件捕获。“DOM2级事件”要求事件应该从document对象开始传播,但是上面的4种浏览器都从window对象开始捕获事件。

- DOM2事件传播机制:
“DOM2级事件”规定的事件流包括3个阶段:事件捕获阶段、处于目标阶段和事件冒泡阶段。首先发生的是事件捕获,这为截获事件提供了机会。接着是实际触发事件的目标。最后是事件冒泡,可以在这个阶段对事件作出响应。还是以前面的HTML代码为例,单击按钮元素后会按照下图所示的顺序触发事件。

![dom2-event.jpg](http://upload-images.jianshu.io/upload_images/2775927-ed8ea5e8022f7637.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

在DOM事件流中实际的目标元素(按钮)在事件捕获阶段不会接受到事件。这意味着在事件捕获阶段,事件从document到<html>再到<body>就停止了。然后接着是“处于目标”阶段,这时事件在按钮上发生,这个阶段被看成是事件冒泡的一部分。最后是事件冒泡阶段,事件又传播回文档中。
虽然“DOM2级事件”规范明确规定捕获阶段不会涉及事件目标,但是Firefox、Chrome、Safari和Opera浏览器都会在捕获阶段触发事件对象上的事件。这将给我们两个机会在目标元素上操作事件。
另外,Firefox、Chrome、Safari和Opera浏览器都支持DOM事件流,但是IE不支持DOM事件流。

###9. 如何阻止事件冒泡? 如何阻止默认事件?

- 在非IE下:
    - 阻止事件冒泡:可以用```event.stopPropagation()```;
    - 阻止默认事件:可以用```event.preventDefault()```;

- 在IE下:
    - 阻止事件冒泡:可以用```event.cancelBubble = true```;
    - 阻止默认事件:可以用```event.returnValue = false```; 

测试如下:

<!DOCTYPE html>
<html class="demo">
<head>
<meta charset="utf-8">
<title>JS Bin</title>
</head>
<body class="demo">
<div id="container" class="demo">
<a href="http://www.baidu.com" id="btn1">点死我</a>
</div>
<script>
addEventHandler($$('#btn1'),'click',function(event){
event.preventDefault();//点击不会跳转,默认行为取消
event.stopPropagation();//由下for循环可知,原本在父容器身上都有事件处理程序,并且会在冒泡阶段执行,但是这里设置了,所以父容器身上接收'click'事件的程序响应就被阻止了;
console.log(this);//
});
for(var i=0; i<$$$('.demo').length;i++) {
addEventHandler($$$('.demo')[i], 'click', function () {
console.log(this.tagName);
});}

//封装DOM对象获取
function $$(el){
return document.querySelector(el);
}
function $$$(els){
return document.querySelectorAll(els);
}

//封装添加事件处理程序
function addEventHandler(node,type,handler){
    if(!node) return false;
    if(node.addEventListener){
        node.addEventListener(type,handler,false);
    }else if(node.attachEvent){
        node.attachEvent('on'+type,handler);
    }else{
        node['on'+type] = handler;
    }
}
//封装删除事件处理程序
function removeEventHandler(node,type,handler){
    if(node.removeEventListener){
        node.removeEventListener(type,handler,false);
    }else if(node.detachEvent){
        node.detachEvent('on'+type,handler);
    }else{
        node['on'+type] = null;
    }
}

</script>
</body>
</html>










推荐阅读更多精彩内容

  • 一、dom对象的innerText和innerHTML有什么区别? innerHTML返回的是从对象起始位置到终止...
    __Qiao阅读 136评论 0 0
  • 问答部分 一、dom对象的innerText和innerHTML有什么区别? innerText~是一个可写属性,...
    dengpan阅读 198评论 1 0
  • dom对象的innerText和innerHTML有什么区别? innerHTML指的是从对象的起始位置到终止位置...
    coolheadedY阅读 204评论 0 0
  • 问题 1.dom对象的innerText和innerHTML有什么区别? innerText: 当使用innerT...
    饥人谷_任磊阅读 199评论 0 1
  • 1.dom对象的innerText和innerHTML有什么区别? innerText是一个可写属性。将写入的内容...
    candy252324阅读 211评论 0 0