聊一聊到底什么是DOM

Web API

  • API(全称baiApplication Programming Interface,即应用程序编程接口)是一些预先定义函数,目的是用来提供应用程序与开发人员基于某软件或者某硬件得以访问一组例程的能力,并且无需访问源码或无需理解内部工作机制细节。
  • 任何开发语言都有自己的API
  • API的特征输入和输出(I/O)例如:var max = Math.max(2, 3, 4)
  • API的使用方法(console.log(“abc”))

Web API 的概念

  • 浏览器提供的一套操作浏览器功能和页面元素的API(BOM和DOM)
  • 此处的Web API特指浏览器提供的API(一组方法),Web API在后面的课程中有其它含义
  • 学习目标:掌握常见浏览器提供的API的调用方式
  • 学习辅助 MDN:https://developer.mozilla.org/zh-CN/docs/Web/API

JS的组成

  • JS
    • ES
    • Web APIs
      • BOM
      • DOM
ES —— JS的核心
  • 定义了JS的语法规范
  • JS的核心,面熟了语言的基本语法和数据类型,ES是一套标准,定义了一种语言的标准与具体实现无关
BOM
  • browser object model,一套操作浏览器功能的 API
  • 通过BOM可以操作浏览器窗口,比如:弹出框、控制浏览器跳转、获取分辨率等
DOM
  • document object model,一套操作页面元素的API
  • DOM 可以把HTML看作是文档树,通过DOM提供的API可以对树上的节点进行操作

DOM的概念

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

DOM树

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

DOM经常进行的操作

  • 获取元素
  • 对元素进行操作(设置其属性或调用其方法)
  • 动态创建元素
  • 事件(什么时机做相应的操作)

根据id获取元素

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

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

console.dir(box)

根据标签名获取元素

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

var box = document.getElementsByTagName("div");

console.log(box)

元素对象内部获取元素

  • 获取的元素对象内部,本身也可以调用根据标签获取元素方法,例如div元素对象也可以调用getElementsByTagName 方法
  • 目的:缩小选择元素的范围,类似css中的后代选择器
<div id="box1">
  <p>text1</p>
</div>
<div id="box2">
  <p>text1</p>
</div>

var box1 = document.getElementById("box1");
var ps1 = box1.getElementsByTagName("p");

根据name属性获取元素

  • 方法:调用document对象的getElementsByName方法
  • 参数:字符串类型的name属性值
  • 返回值:name属性值相同的元素对象组成的数组
  • 不建议使用:在IE和Opera中有兼容问题,会多选中id属性值相同的元素
<input name="age">1~10
<input name="age">11~20
<input name="age">21~31

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

console.log(age)

根据class属性获取元素

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

var box = document.getElementsByClassName("box");

console.log(box)

根据选择器获取元素

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

var box = document.querySelector(".box2");

总结

  • 掌握,没有兼容问题
    • getElementById()
    • getElementsByTagName()
  • 了解
    • getElementsByName()
    • getElementsByClassName()
    • querySelector()
    • querySelectorAll()

事件

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

常用事件监听方法

  1. 绑定HTML元素属性
  <input type="button" value="点我啊" onclick="alert('点我干啥')">
  1. 绑定DOM对象属性
  <input type="button" value="点我啊" id="btn" >

  <script>
    var btn = document.getElementById("btn");
    btn.onclick = function () {
     
     
      alert("点我干啥")
    };
  </script>

事件监听

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

非表单元素属性操作

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

事件函数内部的this

区分不同函数内不this的指向

  • 普通函数 --> window对象
  • 构造函数 --> 生成的实例对象
  • 对象的方法 --> 对象本身
  • 事件函数 --> 事件源

innerHTML和innerText属性

  • 获取标签内部内容的属性有两个: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.id);
  // 元素自定义的新属性不能用点语法直接调用
  console.log(box.age); // undefined
  console.log(box.sex); // undefined
  // 可以调用元素自定义属性的方法
  console.log(box.getAttribute("age"));
  console.log(box.getAttribute("sex"));
</script>

style样式基本操作

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

样式属性操作选择

className类名属性操作

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

对节点操作

生成新的元素对象

var newNode = document.createElement("div");
newNode.innerHTML = "新的div"

添加新的子元素

document.body.appendChild(newNode);

移除一些元素

var box = document.getElementById("box");
document.body.removeChild

获取节点内部的子元素节点

var demo = document.getElementById("demo");
var child1 = demo.children;

节点属性

nodeType:节点的类型,属性值为数字,表示不同的节点类型,共12种,只读

  • 1 元素节点
  • 2 属性节点
  • 3 文本节点

nodeName:节点的名称(标签名称)只读

nodeValue:节点值,返回或设置当前节点的值

  • 元素节点的nodeValue始终是null

元素节点

var box = document.getElementById("box");
console.dir(box) // nodeName: "DIV"   nodeType: 1   nodeValue: null

属性节点

var idNode = box.getAttributeNode("id");
console.dir(idNode);  // nodeName:"id"   nodeType:2   nodeValue:"box"
idNode.nodeValue = "demo";

文本节点

var childNodes = box.childNodes;
console.log(childNodes); // nodeName:"#text"   nodeType:3   nodeValue:"box 1"

节点层级 — 父子节点属性

  • childNodes:只读属性,获取一个节点所有的子节点的实时的集合,集合是动态变化的
  • children:只读属性,返回一个节点所有的子元素节点集合,是一个动态更新的HTML元素集合
  • firstChild:只读属性,返回该节点的第一个子节点,如果该节点没有子节点则返回null
  • lastChild:只读属性,返回该节点的最后一个子节点,如果该节点没有子节点则返回null
  • parentNode:返回一个当前节点的父节点,如果没有这样的节点,比如说像这个节点是树结构的顶端或者没有插入一棵树中,这个属性返回null
  • pareneElement:返回当前节点的父元素节点,如果该元素没有父节点,或者父节点不是一个DOM元素,则返回null
<div id="box">
  <p>段落</p>
  <span>span 小盒子</span>
</div>
<script>
  var box = document.getElementById("box");
  // 获取子节点
  console.log(box.childNodes);  // 获取所有类型的子节点
  console.log(box.children);  // 获取所有元素类型的子节点
  console.log(box.firstChild);  // 获取所有类型的第一个子节点
  console.log(box.lastChild);  // 获取所有类型的最后一个子节点
  console.log(box.lastElementChild);  // 获取所有元素类型的最后一个子节点
  // 获取父级
  console.log(box.parentNode); 
  console.log(box.parentElement);
</script>

节点层级 — 兄弟节点属性

  • nextSibling:只读属性,返回与该节点同级的下一个节点,如果没有返回null
  • previousSibling:只读属性,返回与该节点同级的上一个节点,如果没有返回null
  • nextElementSibling:只读属性,返回与该节点同级的下一个元素节点,如果没有返回null
  • previousElementSibling:只读属性,返回与该节点同级的上一个元素节点,如果没有返回null
  • 注意
    • nextElementSibling 和 previousElementSibling 有兼容性问题,IE9以后才支持
<div id="box">
  <p>段落</p>
  <p>段落</p>
  <p id="p3">段落</p>
  <p>段落</p>
  <p>段落</p>
</div>
<script>
  var p3 = document.getElementById("p3");
  // 上一个兄弟节点
  console.log(p3.previousSibling);
  // 下一个兄弟节点
  console.log(p3.nextSibling);
  // 上一个兄弟元素的节点
  console.log(p3.previousElementSibling);
  // 下一个兄弟元素的节点
  console.log(p3.nextElementSibling);
</script>

创建节点方法

创建新节点的方法

  • document.createElement(“div”) 创建元素节点
  • document.createAttribute(“id”) 创建属性节点
  • document.createTextNode(“hello”) 创建文本节点
  • 一般将创建的新节点存在变量中,方便使用
<div id="box">
  <p>段落1</p>
  <p>段落2</p>
  <p>段落3</p>
  <p>段落4</p>
</div>
<script>
  var div = document.createElement("div");
  
  var cls = document.createAttribute("class");

  var txt = document.createTextNode("hello");

  // 创建的新的节点,是存储在内存中的,但是并没有添加到DOM树中

</script>

添加节点方法

  • parentNode.appendChild(child):将一个节点添加到指定父节点的子节点列表末尾
  • parentNode.replaceChild(newChild, oldChild):用指定的节点替换当前节点的一个子节点,并返回被替换掉的节点
  • parentNode.insertBefore(newNode, referenecNode):在参考节点之前插入一个拥有制定父节点的子节点,referenceNode 必须设置,如果 referenceNode 为 null 则 newNode 将被插入到子节点的末尾
  • parentNode.removeChild(child):移除当前节点的一个子节点。这个子节点必须存在于当前节点中
<div id="box">
  <p>段落1</p>
  <p>段落2</p>
  <p id="p2">段落</p>
  <p>段落3</p>
  <p>段落4</p>
</div>
<script>
  var div = document.createElement("div");
  var cls = document.createAttribute("class");
  var txt = document.createTextNode("hello");

  var box = document.getElementById("box");
  var p2 = document.getElementById("p2");

  box.appendChild(div);
  
  div.appendchild(txt);

  // DOM中的原有节点也可以传给appendChild的参数
  // 1. 将节点从原始位置删除。2.添加到新的指定位置
  // 原因:内存中这个原有节点只有一个,渲染时只能有一个位置
  box.appendchild(p2) 
</script>

替换、插入、删除节点方法

<div id="box">
  <p>段落1</p>
  <p>段落2</p>
  <p id="p2">段落</p>
  <p>段落3</p>
  <p>段落4</p>
</div>
<script>
  var div = document.createElement("div");
  var cls = document.createAttribute("class");
  var txt = document.createTextNode("hello");

  var box = document.getElementById("box");
  var p2 = document.getElementById("p2");

  div.appendchild(txt);

  // 替换节点
  box.placeChild(div, p2);

  // 在某个指定子节点之前插入节点
  box.insertBefore(div, p2);
  box.insertBefore(div, null);  // 添加到box的最后

  // 移除节点
  box.removechild(p2)
</script>

克隆节点

  • Node.cloneNode():克隆一个节点,并且可以选择是否克隆这个节点下的所有内容。参数为Boolean布尔值,表示是否采用深度克隆,如果为 true 则该节点的所有后代节点也都会被克隆,如果为false则只克隆该节点本身,默认值为 true,节点下的内容会被克隆。
  • 注意:克隆时,标签上的属性和属性值也会被复制,写在标签行内的绑定事件可以被复制,但是通过JS动态绑定的事件不会被复制
<div id="box">
  <p>段落1</p>
  <p>段落2</p>
  <p id="p2">段落</p>
  <p>段落3</p>
  <p>段落4</p>
</div>
<script>
  var box = document.getElementById("box");
  var p2 = document.getElementById("p2");

  var newBox = box.cloneNode(false);
  document.body.appendChild(newBox);
</script>

节点判断方法

  • Node.hasChildNodes():没有参数,返回一个 Boolean 布尔值,来表示该元素是否包含有子节点
  • Node.contains(Child):返回一个 Boolean 布尔值,来表示传入的节点是否为该节点的后代节点
<div id="box">
  <p>段落1</p>
  <p>段落2</p>
  <p id="p2">段落</p>
  <p>段落3</p>
  <p>段落4</p>
</div>
<div id="demo"></div>
<script>
  var box = document.getElementById("box");
  var p2 = document.getElementById("p2");
  var demo = document.getElementById("demo");

  console.log(box.hasChildNodes()); // true
  console.log(demo.hasChildNodes());  // false

  console.log(box.contains(p2); // true
  console.log(demo.contains(p2));  // false
</script>

判断方法总结

  • 有三种方法可以判断当前节点是否有子节点
  • node.firstChild !== null
  • node.childNodes.length > 0
  • node.hasChildNodes()

addEventListener 绑定事件方法

  • element.addEventListener()方法
  • 参数:
    • 1 — 事件类型的字符串(直接书写“click”,不需要加 on)
    • 2 — 事件函数
  • 同一个元素可以多次绑定事件监听,同一个事件类型可以注册多个事件函数
  • 兼容性问题:不支持 IE9 以下的浏览器
box.addEventListener("click", function(){
    
    
  console.log(1)
});
box.addEventListener("click", clickEvent);

function clickEvent(){
    
    
  console.log(2)
}

attachEvent 绑定事件方法

  • element.attachEvent()方法
  • 参数:
    • 1 — 事件类型的字符串(需要加 on)
    • 2 — 事件函数
  • 同一个元素可以多次绑定事件监听,同一个事件类型可以注册多个事件函数
  • 兼容性问题:只支持 IE10 及以下的浏览器
box.attachEvent("click", function(){
    
    
  console.log(1)
});
box.attachEvent("click", clickEvent);

function clickEvent(){
    
    
  console.log(2)
}

注册事件的兼容写法

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

addEvent(btn, "click", function () {
    
    
  console.log(1)
})

解除事件绑定

removeEventListener

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

function fn(){
    
    
  console.log(1)
};

box.removeEventListener("click", fn)

detachEvent

  • element.detachEvent() 方法
  • 参数:
    • 1 — 事件类型的字符串(需要加 on)
    • 2 — 事件函数引用名
  • 注意:没有办法移除一个匿名函数,所以在注册事件时需要单独声明一个有函数名的事件函数
  • 兼容性问题:只兼容 IE10 及以下的浏览器
box.attachEvent("click", fn);

function fn(){
    
    
  console.log(1)
};

box.detachEvent("click", fn)

移除事件的兼容写法

  • 自定义一个移除事件函数
  • 参数:事件源,事件类型(不加 on),事件函数
  • IE9 及以上的浏览器,使用 removeEventListener 方法
  • IE9 以下的浏览器,使用 detachEvent 方法
  • 建议:将自己封装的一些常用函数和方法,放到一个单独的 .js 文件中
function removeEvent(ele, type, fn) {
    
    
  if (ele.removeEvent) {
    
    
    ele.removeEvent(type, fn);
  } else if (ele.detachEvent) {
    
    
    ele.detachEvent("on" + type, fn);
  };
};

removeEvent(btn, "click", fn)

事件流的三个阶段

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

事件冒泡应用 — 事件委托

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

事件对象1

  • 只要触发事件,就会有一个对象,内部存储了与事件相关的数据
  • e 在低版本浏览器中有兼容问题,低版本浏览器使用的是 window.event
  • 事件对象常用的属性:
    • e.eventPhone:查看事件触发时所处的阶段
    • e.target:用于获取触发事件的元素
    • e.srcElement:用于获取触发事件的元素,低版本浏览器使用
    • e.currentTarget:用于获取绑定事件的事件源元素
    • e.type:获取事件类型
    • e.clientX / e.clientY:所有浏览器都支持,鼠标距离浏览器窗口左上角的距离
    • e.pageX / e.pageY:IE8 以前不支持,鼠标距离整个 HTML 页面左上顶点的距离

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

  • e.preventDefault():取消默认行为
  • e.returnValue:取消默认行为,低版本浏览器使用
  • e.stopPropagation():阻止冒泡,标准方式
  • e.cancelBubble = true:组织冒泡,IE低版本,标准中已废弃

偏移量属性

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

客户端大小

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

滚动偏移属性

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

猜你喜欢

转载自blog.csdn.net/CS_DGD/article/details/110396327
今日推荐