前端学习-DOM

161 阅读13分钟

DOM

文档对象模型(Document Object Model,简称 DOM),是 W3C 组织推荐的处理可扩展标记语言的标准编程接口。它是一种与平台和语言无关的应用程序接口(API),它可以动态地访问程序和脚本,更新其内容、结构和 www 文档的风格(目前,HTML 和 XML 文档是通过说明部分定义的)。文档可以进一步被处理,处理的结果可以加入到当前的页面。DOM 是一种基于树的 API 文档,它要求在处理过程中整个文档都表示在存储器中。

DOM 树

DOM 又称为文档树模型
文档:一个网页可以称为文档
节点:网页中的所有内容都是节点 (标签、属性、文本、注释等)
元素:网页中的标签
属性:标签的属性

DOM 获取页面元素

根据 id 获取元素

方法:调用 document 对象的 getElementById 方法。
参数:字符串类型的 id 的属性值。
返回值:对应 id 名的元素对象。
注意1:由于 id 名具有唯一性,部分浏览器支持直接使用 id 名访问元素,但不是标准方式,不推荐使用。
注意2:代码执行顺序,如果 js 在 html 结构之前,会导致结构未加载,不能获取对应id的元素。

<p id="para">text</p>

var para = document.getElementById("para");

根据标签名获取元素

方法:调用 document 对象的 getElementsByTagName 方法。
参数:字符串类型的标签名。
返回值:同名的元素对象组成的数组。
注意1:操作数据时需要按照操作数组的方法进行。
注意2:getElementsByTagName 方法内部获取的元素是动态增加的。

<div>div1</div>
<div>div2</div>
<div>div3</div>
  
var divs = document.getElementsByTagName("div");

元素对象内部获取标签元素

获取的元素对象内部,本身也可以调用根据标签获取元素方法,例如 div 元素对象也可以调用getElementsByTagName 方法。
目的:缩小选择元素的范围,类似 css 中的后代选择器。

<div id="box">
    <p>text1 of box1</p>
    <p>text2 of box1</p>
    <p>text3 of box1</p>
    <p>text4 of box1</p>
</div>

var box = document.getElementById("box");
var ps = box.getElementsByTagName("p");

根据 name 获取元素

方法:调用 document 对象的 getElementsByName 方法。
参数:字符串类型的 name 属性值。
返回值:name 属性值相同的元素对象组成的数组。
不建议使用:在 IE 和 Opera 中有兼容问题,会多选中 id 属性值相同的元素。

<input type="radio" name="age">0~10<br>
<input type="radio" name="age">11~20<br>
<input type="radio" name="age">20~30<br>

var ages = document.getElementsByName("age");

根据类名获取元素

方法:调用 document 对象的 getElementsByClassName 方法。
参数:字符串类型的 class 属性值。
返回值:class 属性值相同的元素对象组成的数组。
浏览器兼容问题:不支持 IE8 及以下的浏览器

<p class="para">text2 of box1</p>
<p class="para">text3 of box1</p>

var paras = document.getElementsByClassName("para");

根据选择器获取元素

方法1:调用 document 对象的 querySelector 方法,通过 css 中的选择器去选取第一个符合条件的标签元素。
方法2:调用 document 对象的 querySelectorAll 方法,通过 css 中的选择器去选取所有符合条件的标签元素。
参数:字符串类型的 css 中的选择器。
浏览器兼容问题:不支持 IE8 以下的浏览器

<div id="box">
    <p>text of box</p>
    <p class="para">text2 of box</p>
    <p class="para">text3 of box</p>
    <p>text4 of box</p>
</div>
  
var para = document.querySelector("#box .para");
var paras = document.querySelectorAll("#box .para"); 

总结

掌握,没有兼容问题
getElementById()
getElementsByTagName()

了解
getElementsByName()
getElementsByClassName()
querySelector()
querySelectorAll()



事件

事件:在什么时候做什么事
执行机制:触发--响应机制
绑定事件(注册事件)三要素:
1、事件源:给谁绑定事件
2、事件类型:绑定什么类型的事件
3、事件函数:事件发生后执行什么内容,写在函数内部

事件监听

JavaScript 解析器会给有绑定事件的元素添加一个监听,解析器会一直监测这个元素,只要 触发对应的绑定事件,会立刻执行事件函数。

方法1:绑定 HTML 元素属性。
方法2:绑定 DOM 对象属性。

常用的鼠标事件类型

方法名描述
onclick鼠标左键单击触发
ondbclick鼠标左键双击触发
onmousedown鼠标按键按下触发
onmouseup鼠标按键放开时触发
onmousemove鼠标在元素上移动触发
onmouseover鼠标移动到元素上触发
onmouseout鼠标移出元素边界触发
<input type="button" id="btn" value="button">
<script>
    var btn = document.getElementById("btn");
    
    btn.onclick = function () {
        alert("123");
    };
</script>

DOM 元素属性操作

非表单元素的属性

例如:href、title、id、src 等。
调用方式:元素对象打点调用属性名,例如 obj.href。
注意:部分的属性名跟关键字和保留字冲突,会更换写法。
class → className
for → htmlFor
rowspan → rowSpan
属性赋值:给元素属性赋值,等号右侧的赋值都是字符串格式。

获取标签内部内容的属性

获取标签内部内容的属性有两个:innerHTML 和 innerText
innerHTML属性,在获取标签内部内容时,如果包含标签,获取的内容会包含标签,获取的内容包括空白换行等。
innerText属性,在获取标签内部内容时,如果包含标签,获取的内容会过滤标签,获取的内容会去掉换行和缩进等空白。

更改标签内容

还可以通过两个属性给双标签内部去更改内容:
innerHTML 设置属性值,有标签的字符串,会按照 HTML 语法中的标签加载。
innerText 设置属性值,有标签的字符串,会按照普通的字符加载。

对比使用场景
innerText:在设置纯字符串时使用。
innerHTML:在设置有内部子标签结构时使用。

表单元素属性

value 用于大部分表单元素的内容获取(option除外)
type 可以获取input标签的类型(输入框或复选框等)
disabled 禁用属性
checked 复选框选中属性
selected 下拉菜单选中属性
注意:在 DOM 中元素对象的属性值只有一个时,会被转成布尔值显示。
例如:txt.disabled = true;

自定义属性操作

getAttribute(name) 获取标签行内属性
setAttribute(name,value) 设置标签行内属性
removeAttribute(name) 移除标签行内属性
与element.属性的区别: 上述三个方法用于获取任意的行内属性,包括自定义的属性。

<div id="box" age="18" sex="male">小明</div>
<script>

  var box = document.getElementById("box");
  
  // 元素自定义的新属性不能用点语法直接调用
  // 可以调用元素对象的获取自定义属性的方法
  console.log(box.getAttribute("age"));
  // 也可以调用自有的属性
  console.log(box.getAttribute("id"));

  // 设置属性,添加新的自定义属性或者自有属性
  box.setAttribute("age","20");
  box.setAttribute("city","Beijing");
  
  // 传的参数不需要进行属性名的修改
  box.setAttribute("class","demo");

  // 移除属性
  box.removeAttribute("age");
  box.removeAttribute("class");
</script>

style 样式属性操作

使用 style 属性方式设置的样式显示在标签行内。
element.style 属性的值,是所有行内样式组成的一个样式对象。
样式对象可以继续点语法调用或更改 css 的行内样式属性,例如 width、height 等属性。
注意1:类似 background-color 这种复合属性的单一属性写法,是由多个单词组成的,要修改为驼峰命名方式书写 backgroundColor。
注意2:通过样式属性设置宽高、位置的属性类型是字符串,需要加上 px 等单位。

<input type="button" value="按钮" id="btn">
<div id="box">文字</div>

<script>
    var btn = my$("btn");
    var box = my$("box");
    
    btn.onclick = function () {
      box.style.width = "100px";
      box.style.height = "200px";
      box.style.backgroundColor = "yellow";
    };
</script>

className 类名属性操作

修改元素的 className 属性相当于直接修改标签的类名。
如果需要修改多条 css 样式,可以提前将修改后的样式设置到一个类选择器中,后续通过修改类名的方式,批量修改 css 样式。 修改单个class可以用replace方法。

box.className.replace("hide","show");

DOM 节点操作

节点属性

nodeType 节点的类型,属性值为数字,表示不同的节点类型,共 12 种,只读
1 元素节点
2 属性节点
3 文本节点
nodeName 节点的名称(标签名称),只读
nodeValue 节点值,返回或设置当前节点的值
元素节点的 nodeValue 始终是 null

父子节点常用属性

childNodes 只读属性,获取一个节点所有子节点的实时的集合,集合是动态变化的。
children 只读属性,返回一个节点所有的子元素节点集合,是一个动态更新的 HTML 元素集合。
firstChild 只读属性,返回该节点的第一个子节点,如果该节点没有子节点则返回 null。
lastChild 只读属性,返回该节点的最后一个子节点,如果该节点没有子节点则返回 null。
parentNode 返回一个当前节点的父节点,如果没有这样的节点,比如说像这个节点是树结构的顶端或者没有插入一棵树中,这个属性返回 null。
parentElement 返回当前节点的父元素节点,如果该元素没有父节点,或者父节点不是一个 DOM 元素,则返回 null。

兄弟节点常用属性

nextSibling 只读属性,返回与该节点同级的下一个节点,如果没有返回null。
previousSibling 只读属性,返回与该节点同级的上一个节点,如果没有返回null。
nextElementSibling 只读属性,返回与该节点同级的下一个元素节点,如果没有返回null。
previousElementSibling 只读属性,返回与该节点同级的上一个元素节点,如果没有返回null。

注意:nextElementSibling 和 previousElementSibling 有兼容性问题,IE9以后才支持。

创建新节点的方法

document.createElement("div") 创建元素节点
document.createAttribute("id") 创建属性节点
document.createTextNode("hello") 创建文本节点
一般将创建的新节点存在变量中,方便使用。

节点常用操作方法 1

parentNode.appendChild(child):将一个节点添加到指定父节点的子节点列表末尾。
parentNode.replaceChild(newChild, oldChild):用指定的节点替换当前节点的一个子节点,并返回被替换掉的节点。
parentNode.insertBefore(newNode, referenceNode):在参考节点之前插入一个拥有指定父节点的子节点,referenceNode 必须设置,如果 referenceElement 为 null 则 newNode 将被插入到子节点的末尾。
parentNode.removeChild(child):移除当前节点的一个子节点。这个子节点必须存在于当前节点中。

节点常用操作方法 2

Node.cloneNode(Boolean):克隆一个节点,并且可以选择是否克隆这个节点下的所有内容。参数为 Boolean 布尔值,表示是否采用深度克隆,如果为 true,则该节点的所有后代节点也都会被 克隆,如果为 false,则只克隆该节点本身,默认值为true,节点下的内容会被克隆。
注意:克隆时,标签上的属性和属性值也会被复制,写在标签行内的绑定事件可以被复制, 但是通过 JavaScript 动态绑定的事件不会被复制。

节点常用操作方法 3

Node.hasChildNodes():没有参数,返回一个 Boolean 布尔值,来表示该元素是否包含有子节点。
Node.contains(child):返回一个 Boolean 布尔值,来表示传入的节点是否为该节点的后代节点。

DOM 事件详解

注册事件的方法1

element.addEventListener() 方法。
参数:
第一个参数:事件类型的字符串(直接书写”click”,不需要加 on)
第二个参数:事件函数
同一个元素可以多次绑定事件监听,同一个事件类型可以注册多个事件函数
兼容性问题:不支持 IE9 以下的浏览器

<input type="button" value="点击" id="btn">
<script>
  var btn = document.getElementById("btn");

  // DOM 2 级事件绑定方式
  btn.addEventListener("click",function () {
    alert(1);
  });
  // 多次绑定相同的事件类型,事件会根据书写的顺序进行一个事件排队
  btn.addEventListener("click",clickEvent);
  function clickEvent() {
    alert(2);
  }
  // 方法不支持 IE 9 以下的浏览器
  </script>

注册事件的方法2

element.attachEvent() 方法。
参数: 第一个参数:事件类型的字符串(需要加 on)
第二个参数:事件函数
同一个元素可以多次绑定事件监听,同一个事件类型可以注册多个事件函数
兼容性问题:只支持 IE10 及以下的浏览器

<input type="button" value="点击" id="btn">
<script>
  var btn = document.getElementById("btn");    
  // DOM 2 级事件绑定方式
  // 兼容:IE 10 及以下浏览器
  // IE8 及以下的浏览器处理事件队列时,会出现顺序错乱
  btn.attachEvent("onclick",function () {
    alert(3);
  });
  btn.attachEvent("onclick",clickEvent);
  function clickEvent() {
    alert(4);
  }
</script>

注册事件的兼容写法

自定义一个注册事件函数
参数:事件源,事件类型(不加 on),事件函数
IE9 及以上的浏览器,使用 addEventListener 方法
IE9 以下的浏览器,使用 attachEvent 方法
判断浏览器时,不需要判断它的版本,可以检测浏览器能力
浏览器能力检测:将某个方法的调用作为 if 语句的判断条件,如果浏览器认识该方法返回true,否则返回 false。

<input type="button" value="点击" id="btn">
<script>
  var btn = document.getElementById("btn");  
  // 调用函数
  addEvent(btn,"click",function () {
    alert(1);
  }); 
  // DOM 2 级事件绑定方式
  // 自己制作一个兼容所有浏览器的绑定事件的函数
  // 参数:事件源,事件类型,事件函数
  function addEvent(ele,type,fn) {
    // IE 9 及以上的浏览器和其他浏览器,使用 addEventListener 方法
    // IE 9 以下的浏览器,使用 attachEvent 方法
    // 浏览器能力检测
    if (ele.addEventListener) {
      ele.addEventListener(type,fn);
    } else if (ele.attachEvent) {
      ele.attachEvent("on" + type,fn);
    }
  }
  </script>

移除事件的其他方法1

element.removeEventListener() 方法。
参数:
第一个参数:事件类型的字符串(直接书写”click”,不需要加 on)
第二个参数:事件函数引用名
注意:没有办法移除一个匿名函数,所以在注册事件时需要单独声明一个有函数名的事件函数。
兼容性问题:不支持 IE9 以下的浏览器

<input type="button" value="点击" id="btn">
<script>
  var btn = document.getElementById("btn");    
  
  //绑定事件  
  btn.onclick = function () {
    alert(1);
  };
  //解除绑定方法
  btn.onclick = null;
  
  //绑定事件
  btn.addEventListener("click",fun);
  //解除绑定
  btn.removeEventListener("click",fun);

  function fun() {
    alert(2);
  }
</script>

移除事件的其他方法2

element.detachEvent() 方法。
参数: 第一个参数:事件类型的字符串(需要加 on) 第二个参数:事件函数
注意:没有办法移除一个匿名函数,所以在注册事件时需要单独声明一个有函数名的事件函数。
兼容性问题:只支持 IE10 及以下的浏览器

<input type="button" value="点击" id="btn">
<script>
    var btn = document.getElementById("btn");    
   
    // 绑定事件
    btn.attachEvent("onclick",fun);
    // 解除绑定
    btn.detachEvent("onclick",fun);
    function fun() {
      alert(2);
    }
    
</script>

移除事件的兼容写法

自定义一个移除事件函数
参数:事件源,事件类型(不加 on),事件函数
IE9 及以上的浏览器,使用 removeEventListener 方法
IE9 以下的浏览器,使用 detachEvent 方法
建议:将自己封装的一些常用函数和方法,放到一个单独的 .js 文件中。

<input type="button" value="点击" id="btn">
<script>
  var btn = document.getElementById("btn");  
  // 调用函数
  addEvent(btn,"click",fun); 
  // 移除事件                
  removeEvent(btn,"click",fun);
  
  function fun() {
    alert(1);
  }
  
  // DOM 2 级事件绑定方式
  // 自己制作一个兼容所有浏览器的绑定事件的函数
  // 参数:事件源,事件类型,事件函数
  function addEvent(ele,type,fn) {
    // IE 9 及以上的浏览器和其他浏览器,使用 addEventListener 方法
    // IE 9 以下的浏览器,使用 attachEvent 方法
    // 浏览器能力检测
    if (ele.addEventListener) {
      ele.addEventListener(type,fn);
    } else if (ele.attachEvent) {
      ele.attachEvent("on" + type,fn);
    }
  }

  // 兼容所有浏览器的 解除绑定事件的函数
  // 参数:事件源,事件类型,事件函数
  function removeEvent(ele,type,fn) {
    // 浏览器能力检测
    if (ele.removeEventListener) {
      ele.removeEventListener(type,fn);
    } else if (ele.detachEvent) {
      ele.detachEvent("on" + type,fn);
    }
  }
  </script>

DOM 事件流

事件流的三个阶段

第一个阶段:事件捕获
第二个阶段:事件执行过程
第三个阶段:事件冒泡
addEventListener() 第三个参数为 false 时,事件冒泡
addEventListener() 第三个参数为 true 时,事件捕获
onclick 类型:只能进行事件冒泡过程,没有捕获阶段
attachEvent() 方法:只能进行事件冒泡过程,没有捕获阶段

<div id="box1">
    <div id="box2">
        <div id="box3"></div>
    </div>
</div>

<script>
    // 获取元素
    var box1 = document.getElementById("box1");
    var box2 = document.getElementById("box2");
    var box3 = document.getElementById("box3");

    // 添加点击事件
    // addEventListener 有第三个参数,用来决定事件流的方向
    // 参数值是 布尔类型的值,false 表示事件冒泡过程,true 表示事件捕获过程
    // 参数默认值是 false
    box1.addEventListener("click",function () {
        console.log(1);
    },false);
    box2.addEventListener("click",function () {
         console.log(2);
    },false);
    box3.addEventListener("click",function () {
         console.log(3);
    },false);
    box1.addEventListener("click",function () {
         console.log(this.id);
    },true);
    box2.addEventListener("click",function () {
         console.log(this.id);
    },true);
    box3.addEventListener("click",function () {
         console.log(this.id);
    },true);


    // onclick 属性添加事件方法只有冒泡过程,没有捕获过程
    box1.onclick = function () {
        console.log(1);
    };

</script>

事件委托

利用事件冒泡,将子级的事件委托给父级加载
同时,需要利用事件函数的一个 e 参数,内部存储的是事件对象

<ul id="list">
    <li>1</li>
    <li>2</li>
    <li>3</li>
    <li>4</li>
    <li>5</li>
</ul>

<script>
    // 让每个 li 被点击后,自己添加特殊的背景色,而其他兄弟不添加
    // 以前的思路:获取所有的 li 标签元素,批量添加事件
    // 事件委托:可以将一些子级的公共类型的事件委托给他们的父级添加,在父级内部想办法找到真正触发事件的最底层的事件源
    // 获取元素
    var list = document.getElementById("list");
    var lis = list.children;
    // 给 ul 添加点击事件
    list.onclick = function (e) {
        // 在内部要想办法找到真正触发事件的 li
        // 借用事件函数内部的一个参数 e,e 是事件对象
        // 只要触发事件,函数内部都可以得到一个事件对象,对象中存储了关于事件的一系列数据
        // e.target 属性记录的就是真正触发事件的事件源
        // 排除其他
        for (var i = 0 ; i < lis.length ; i++) {
            lis[i].style.backgroundColor = "";
        }
        e.target.style.backgroundColor = "pink";
    };
</script>

事件对象

只要触发事件,就会有一个对象,内部存储了与事件相关的数据。
e 在低版本浏览器中有兼容问题,低版本浏览器使用的是 window.event
事件对象常用的属性:

方法名描述
e.eventPhase查看事件触发时所处的阶段
e.target用于获取触发事件的元素
e.srcElement用于获取触发事件的元素,低版本浏览器使用
e.currentTarget用于获取绑定事件的事件源元素
e.type获取事件类型
e.clientX/e.clientY所有浏览器都支持,鼠标距离浏览器窗口左上角的距离
e.pageX/e.pageYIE8 以前不支持,鼠标距离整个HTML页面左上顶点的距离
<div id="box1">
    <div id="box2">
        <div id="box3"></div>
    </div>
</div>
<script>
    // 获取元素
    var box1 = document.getElementById("box1");
    var box2 = document.getElementById("box2");
    var box3 = document.getElementById("box3");

    // 添加事件
    box1.onclick = function (e) {
        // e指的就是存储事件对象的参数,只要事件被触发,e就会自动接收数据
        // 兼容问题
        e = e || window.event;
        // e.eventPhase 判断出事件执行时处于哪个阶段
        // 1:捕获阶段
        // 2:目标阶段
        // 3:冒泡阶段
        // console.log(e.eventPhase);

        // 获取真正触发事件的元素
        var target = e.target || e.srcElement;
        // console.log(target);
        // 获取绑定事件的事件源元素
        console.log(e.currentTarget);
        // this 指向
        console.log(this);
    };

</script>
<div id="box1"></div>
<script>
    // 获取元素
    var box1 = document.getElementById("box1");

    box1.onmouseover = fn;
    box1.onmouseout = fn;
    // 避免添加多个函数,占用更多的内存
    function fn(e) {
        e = e || window.event;
        // 根据事件类型,执行不同的代码
        switch (e.type) {
            case "mouseover":
                box1.style.backgroundColor = "pink";
                break; 
            case "mouseout":
                box1.style.backgroundColor = "yellowgreen";  
                break;                   
        }
    }
</script>

取消默认行为和阻止事件传播的方式

方法名描述
e.preventDefault()取消默认行为
e.returnValue取消默认行为,低版本浏览器使用
e.stopPropagation();阻止冒泡,标准方式
e.cancelBubble = true;阻止冒泡,IE 低版本,标准中已废弃
<a id="link" href="www.baidu.com">点击</a>
<script>
  var link = document.getElementById('link');
  link.onclick = function (e) {
    e = e || window.event;
    alert('hello');
    // 普通的方式阻止默认行为
    // return false;
    // DOM 的方法
    // e.preventDefault();
    // 低版本浏览器需要使用一个对象的属性
    e.returnValue = false;
  }
</script>
<div id="box1">
  <div id="box2">
    <div id="box3">
    </div>
  </div>
</div>

<script>
  // 事件冒泡
  var box1 = document.getElementById('box1');
  var box2 = document.getElementById('box2');
  var box3 = document.getElementById('box3');

  var array = [box1, box2, box3];

  for (var i = 0; i < array.length; i++) {
    var box = array[i];
    box.onclick = function (e) {
      e = e || window.event;
      console.log(this.id);
      // 阻止事件冒泡
      // e.stopPropagation();
      // 低版本浏览器使用 属性
      e.cancelBubble = true;
    }
  }
</script> 

DOM 特效

偏移量属性

offsetParent 偏移参考父级,距离自己最近的有定位的父级,如果都没有定位参考body(html)
offsetLeft / offsetTop 偏移位置
offsetWidth / offsetHeight 偏移大小

客户端大小

client 系列没有参考父级元素。
clientLeft / clientTop 边框区域尺寸,不常用
clientWidth / clientHeight 边框内部大小

滚动偏移属性

scrollLeft / scrollTop 盒子内部滚动出去的尺寸
scrollWidth / scrollHeight 盒子内容的宽度和高度