JS学习笔记(十二)DOM

JS学习笔记(十二)DOM

文章目录

一、DOM

1.1 Document类型

Document类型表示 JS 文档中表示文档节点的类型,Document节点的特点:

  • nodeType等于9
  • nodeName值为“#document”
  • nodeValue值为 null
  • parentNode值为null
  • ownerDocument值为null

1.2 DOM树

  • 文档:一个页面就是一个文档,DOM中使用document表示
  • 元素:页面中所有的标签都是元素,DOM中使用element表示
  • 节点:网页中的**所有内容 **都是节点(标签、属性、文本、注释等),DOM中使用node表示

注意:DOM把以上内容都看作对象

1.3 获取文档子节点 body 和 html

(一)获取body

document.body

(二)获取html

document.documentElement
let html = document.documentElement;
console.log(html === document.childNodes[0]); //true
console.log(html === documentfirstChild); //true

1.4 文档信息

提供浏览器所加载的网页信息

属性有:title、URL、domain、referrer

  • URL:地址栏中的URL
  • domain:包含页面的域名
  • referrer:包含链接到当前页面的那个页面的URL

1.5 获取元素

获取元素的方式:

  • 根据ID获取
  • 根据标签名获取
  • 通过HTML5新增的方法获取
  • 特殊元素获取

1.5.1 根据ID获取

通过 getElementsById() 获取

1.5.2 根据标签名获取

通过 getElementsByTagName() 获取,返回一个包含零个或多个元素的NodeList

获得文档内所有元素:

let allElement = document.getElementByTagName("*");

1.5.3 HTML5新增的获取方式

  • getElementsByClassName
document.getElementsByClassName('类名');
  • querySelector 和 querySelectorAll
document.querySelector('选择器'); //根据指定的选择器返回第一个元素对象
document.querySelectorAll('选择器');//根据指定的选择器返回所有元素对象
  • matches()

若元素匹配该选择符则返回true,否则返回false

if (document.body.matches("body.page1")) {
    
    
    // true
}

1.6 特殊集合

  • document.anchors:包含文档中所有带name属性的<a>
  • document.forms:包含文档中所有form元素
  • document.images:包含文档中所有img元素
  • document.links:包含文档中所有带href属性的的元素

1.7 文档写入

向网页输入流写入内容:write()、writeln()、open()、close()。

  • write()就简单的写入文本,writeln()会在字符串末尾追加一个换行符。
  • write()和 writeln()经常用于动态包含外部资源

二、节点

节点至少拥有nodeType、nodeName 和 nodeValue属性

2.1 Node类型

       每个节点都有nodeType属性,表示该节点的类型,节点类型由定义再Node类型上的12个数值常量表示:

  • Node.ELEMENT_NODE(1) 元素节点
  • Node.ATTRIBUTE_NODE(2) 属性节点
  • Node.TEXT_NODE(3) 文本节点(包含文字、空格、换行等)
  • Node.CDATA_SECTION_NODE(4)
  • Node.ENTITY_REFERENCE_NODE(5)
  • Node.ENTITY_NODE(6)
  • Node.PROCESSING_INSTRUCTION_NODE(7)
  • Node.COMMENT_NODE(8)
  • Node.DOCUMENT_NODE(9)
  • Node.DOCUMENT_TYPE_NODE(10)
  • Node.DOCUMENT_FRAGMENT_NODE(11)
  • Node.NOTATION_NODE(12)

2.1.1 nodeName 和 nodeValue

       nodeName 和 nodeValue保存着有关节点的信息。这两个属性的值完全取决于节点的类型,在使用之前最好先检测节点类型

2.2 节点关系

       每个节点都有childNodes属性,其中包含一个NodeList的实例。ownerDocument 属性是一个指向代表整个文档的文档节点的指针。是所有节点都共享的关系

2.3 节点操作

2.3.1 父节点的操作

孩子节点对象.parentNode

2.3.2 子节点的操作

基本操作
1.parentNode.childNodes

       返回包含指定节点的子节点集合,返回值里包含了所有的子节点,包括元素节点,文本节点等,若只想获得里面的元素节点还需要进一步处理,所以一般不使用childNodes
 

2.parentNode.children

       parentNode.children是只读属性,返回所有的子元素节点,只返回子元素节点,其他的不返回。

第一个子元素和最后一个子元素的获取
3..parentNode.firstChild
4.parentNode.lastChild

以上两种方法包括所有的节点

5.parentNode.firstElementChild

只返回第一个子元素节点,找不到则返回null

6.parentNode.lastElementChild

firstElementChild 和 lastElementChild有兼容性问题。

 
更好的方案(实际开发的写法)

既不会有兼容性问题又可以返回第一个子元素

parentNode.children[0]

2.3.3 兄弟节点

1.node.nextSibling

返回当前元素的下一个兄弟节点,包括所有节点

2.node.previousSibling

返回当前元素的上一个兄弟节点,包括所有节点

3.node.nextElementSibling

返回当前元素的下一个兄弟元素节点

4.node.previousElementSibling

同样这两个方法有兼容性问题

 
解决方案:

自己封装一个兼容性函数

function getNextElementSibling(element) {
    
    
    var el = element;
    while (el = el.nextSibling) {
    
    
        if (el.nodeType === 1) {
    
    
            return el;
        }
    }
    return null;
}

2.3.4 创建节点

document.createElement('tagName')

创建的元素原先不存在,是根据要求动态生成的,所以也称动态创建元素节点

2.3.5 添加节点

1.node.appenChild(child)

       将一个节点添加到指定父节点的子节点的列表末尾

       如果把文档中已经存在的节点传给appendChild(),则这个节点会从之前的位置被转移到新位置。即若调用appendChild()传入父元素的第一个节点,则这个节点会成为父元素的最后一个节点

//假设someNode有多个节点
let returnedNode = someNode.appendChild(someNode.firstChild);
alert(returnedNode == someNode.firstChild);//false
alert(returnedNode == lastChild);//true
2.node.insertBefore(child,指定元素)

将一个节点添加到指定父节点的指定子节点前面

2.3.6 删除节点

node.removeChild(child)

从DOM中删除一个子节点,返回删除的节点

2.3.7 复制节点

node.cloneNode()

返回调用该方法的节点的一个副本,也称克隆节点/拷贝节点

注意:

  • 若括号里为空或者false,则是浅拷贝,及只复制节点本身,不克隆里面的子节点
  • 若括号参数为true,则是深度拷贝,会复制节点本身及里面的所有子节点

2.4 Element类型 (元素)

Element 类型的节点特征:

  • nodeType 等于1
  • nodeName 值为元素的标签名
  • nodeValue 等于 null
  • parentNode 值为 Document 或 Element对象
  • 子节点可以是Element、Text、Comment、ProcessingInstruction、CDATASection、EntityReference类型

可以通过nodeName或tagName属性来获取元素的标签名,但注意,div.tagName 返回的是“DIV”而不是“div”,HTML中,元素标签名始终以全大写表示。

2.4.1 获取属性值

  • element.属性
  • element.getAttribute(‘属性’);

区别:

  • element.属性 :获取内置属性值(元素自带的属性)
  • element.getAttribute(‘属性’):主要获取自定义属性

2.4.2 设置属性值

  • element.属性 = ‘值’ 设置内置属性值
  • element.setAttribute(‘属性’, ‘值’); 主要针对自定义属性
div.id = 'test';
div.className = 'navs';

div.setAttribute('index',2);
div.setAttribute('class','footer'); //这里写的是class 不是className

注意:class在两种方法中的写法

2.4.3 移除属性

removeAttribute(属性);

2.4.4 自定义属性

目的:为了保存并使用数据,有些数据可以保存到页面中而不用保存到数据库中。

自定义属性通过getAttribute(‘属性’)获取

1. 设置H5自定义属性

H5规定自定义属性以 **data- 开头 **做属性名并赋值

<div data-index= '1'></div>
//或使用JS设置
element.setAttibute("data-index",2);
<div getTime="20" data-index="2" data-list-name="niki"></div>
<script>
    var div = document.querySelector("div");
    console.log(div.dataset.listName);
    console.log(div.dataset["listName"]);
</script>
  • dataset 是一个集合,里面存放了所有以data开头的自定义属性
  • 若自定义属性里面有多个- 链接的单词,我们获取的时候采取驼峰命名法

2. 获取H5自定义属性

  • 兼容性获取:element.getAttribute(‘data-index’);
  • H5新增 element.dataset.indexelement.dataset[‘index’] IE11 才开始支持

2.4.5 attribute 属性

       Element是唯一一个使用attribute属性的DOM 节点类型。attribute 属性包含一个NamedNodeMap实例,其包含以下方法:

  • getNamedItem(name):返回nodeName属性等于name的节点
  • removeNamedItem(name):删除nodeName属性等于name的节点
  • setNamedItem(node):向列表中添加node节点,以其nodeName为索引
  • item(pos):返回索引位置pos出的节点

       attributes属性中的每个节点的nodeName是对应属性的名字,nodeValue 是属性的值,如要去的元素id属性的值

let id = element.attributes.getNamedItem("id").nodeValue;
let id = element.attributes.getNamedItem["id"].nodeValue;

2.4.6 表单属性设置

案例:模仿京东显示隐藏密码

  1. 核心思路:点击眼睛按钮,把密码框类型改为文本框就可以看见里面的密码
  2. 一个按钮两个状态,点击一次,切换为文本框,继续点击一次切换为密码框
  3. 算法:利用一个flag遍历,判断flag的值,如果时1就切换为文本框,flag设置为0,如果时0就切换为密码框,flag设置为1
let eye = document.getElementById("eye");
let pwd = document.getElementById("pwd");
// 注册事件处理程序
let flag = 0;
eye.addEventListener("click", () => {
    
    
  // 点击一次后,flag一定变化
  if(flag == 0) {
    
    
    pwd.type = 'text';
    eye.src = 'images/open.png';
    flag = 1;
  } else {
    
    
    pwd.type = 'password';
    eye.src = 'images/close.png';
    flag = 0;
  }
})

2.4.7 修改样式

1.element.style       行内样式操作
2.element.className   类名样式操作

注意:

  • JS 里面的样式采取驼峰命名法,如:fontSize
  • JS 修改 style 样式操作,产生的是行内样式,css 权重比较高
  • 如果样式较多,可以采取操作类名方式更改元素样式
  • class因为是个保留字,因此使用className来操作元素类名属性
  • className 会直接更改元素的类名,会覆盖原来的类名

2.5 Text类型

Text类型的节点的特征:

  • nodeType 等于3
  • nodeName 值为“#text”;
  • nodeValue 值为节点中包含的文本
  • parentNode 值为Element对象

       Text节点中包含的文本可以通过nodeValue属性访问,也可通过data属性访问,这两个属性包含相同的值
 

2.5.1 创建文本节点

document.createTextNode()

2.5.2 规范化文本节点

normalize()

用来合并相邻的文本节点。

let element = document.createElement("div");
element.className="mes";
let textNode = document.createTextNode("hello world!");
element.appendChild(textNode);

let anotherTextNode = document.createTextNode("niki");
element.append(anotherTextNode);

document.body.appendChild(element);
alert(element.childNodes.length); //2
element.normalize();
alert(element.childNodes.length);//1
alert(element.firstChild.nodeValue)//"hello world!niki"

2.5.3 拆分文本节点

splitText()

该方法在指定的偏移位置拆分nodeValue,将一个文本节点拆分为两个文本节点

三、 DOM编程

3.1 NodeList

NodeList 就是基于DOM文档的实时查询,以下行为会导致无穷循环:

let divs = document.getElementsByTagName("div");
for (let i = 0; i < divs.length; ++i) {
    
    
    let div = document.createElement("div");
    document.body.appendChild(div)
}

解决方案:

let divs = document.getElementsByTagName("div");
for (let i = 0, len = divs.length; i < len; ++i) {
    
    
    let div = document.createElement("div");
    document.body.appendChild(div)
}

四、MutationObserver接口

       MutationObserver接口可以在DOM被修改时异步执行回调。使用MutationObserver可以观察整个文档、DOM树的一部分或某个元素。此外还可以观察元素属性、子节点、文本或前三者任意组合的变化

4.1 基本用法

       MutationObserver 的实例要通过调用MutationObserver构造函数并传入一个回调函数来创建

let observer = new MutationObserver(() => console.log("DOM is mutataed!"));

1. observer()

       新创建的MutationObserver 实例不会关联DOM的任何部分,要用observer与DOM关联。observer()接收两个必须的参数:要观察其变化的DOM节点,以及一个MutationObserver 对象(用于控制观察哪些方面的变化,是一个键值对形式配置选项的字典

let observer = new MutationObserver(() => console.log("<body> attributes changed!"))
observer.observe(document.body,{
    
    attributes:true});

document.body.className ='foo';
console.log('changed body class');
//changed body class
//<body> attributes changed!

       回调中的console后执行,表明回调并非与实际的DOM变化同步进行。

2. 回调与MutationRecord

       每个回调都会收到一个按顺序入队的MutationRecord实例的数组。MutationRecord 实例包含的信息包括发生了什么变化,以及DOM的哪一部分收到了影响。,传给回调函数的第二个参数是观察变化的MutationObserver实例

let observer = new MutationObserver(
	(mutationRecords) => console.log(mutationRecord));

3. disconnect()

       默认情况,只要被观察的元素不被垃圾回收,MutationObserver的回调就会响应DOM变化事件,从而被执行。要提前终止执行回调,可以用disconnect(),该方法不仅会停止此后变化事件的回调,也会抛弃已经加入任务队列要异步执行的回调:

let observer = new MutationObserver(() => console.log("<body> attributes changed!"))
observer.observe(document.body,{
    
    attributes:true});

document.body.className = 'foo';
observer.disconnect();
observer.body.className = 'bar';
// 无日志输出

       要想让已经加入任务队列的回调执行,可以使用setTimeout()让已经入列的回调执行完毕再调用disconnect()

let observer = new MutationObserver(() => console.log("<body> attributes changed!"))
observer.observe(document.body,{
    
    attributes:true});

document.body.className = 'foo';
setTimeout(() => {
    
    
    observer.disconnect();
	observer.body.className = 'bar';
},0)
// <body> attributes changed!

4. 复用MutationObserver

       多次调用observe(),可以服用一个MutationObserver对象观察多个不同的目标节点。此时,MutationRecord的target属性可以标识发生变化事件的目标节点

let observer = new MutationObserver(
    (mutationRecords) => console.log(mutationRecords.map((x) => x.target)));
let childA = document.createElement("div");
let childB = document.createElement("span");
document.body.appendChild(childA);
document.body.appendChild(childB);

// 观察两个节点
observer.observe(childA, {
    
    attributes:true});
observer.observe(childB, {
    
    attributes:true});

// 修改两个节点的属性
childA.setAttribute("foo","bar");
childB.setAttribute("foo","bar");

5. 重用MutationObserver

       调用disconnect()并不会结束MutationObserver的生命,还可以重新使用这个观察者,再将他关联到新的目标节点

let observer = new MutationObserver(() => console.log("<body> attributes changed!"))
observer.observe(document.body,{
    
    attributes:true});

document.body.setAttribute("foo","bar") // 触发事件
setTimeout(() => {
    
    
    observer.disconnect();
    document.body.setAttribute("bar","baz"); // 不会触发事件
},0);

setTimeout(() => {
    
    
    observer.observe(document.body,{
    
    attributes:true});
    document.body.setAttribute("baz","qux"); // 触发事件
},0);

4.2 MutationObserverInit与观察范围

       MutationObserverInit 对象用于控制对目标节点的观察范围,观察的事情包括属性变化、文本变化和子节点变化

4.3 异步回调与记录队列

1. 记录队列

       每次MutationRecord 被添加到MutationObserver的记录队列时,仅当之前没有已排期的微任务回调时,才会将观察者注册的回调作为微任务调度到任务队列上,这样可以保证记录队列的内容不会被回调处理两次

2. takeRecords()

takeRecords()可以清空记录队列,去除并返回其中所有的MutationRecord实例

五、HTML5

5.1 CSS类扩展

5.1.1 getElementByClassName()

见1.5.3

5.1.2 ClassList 属性

1. 朴素的操作(不建议使用)

       可通过className 实现类名的添加、删除和替换。className也是一个字符串,所以每次操作后都要重新设置这个值才能生效

<div class="bd user disable"></div>

       该div有3个类名。要想删除其中一个,就得先把className拆开,删除不想要的那个,再把包含剩余类的字符串设置回去:

// 要删除的类
let targetClass = 'user';
// 把类名拆成数组
let classNames = div.className.split(/\s+/);
// 找到要删除类名的索引
let idx = classNames.indexOf(targetClass);
// 若有就删除
if(idx>-1) {
    
    
    classNames.splice(idx,1);
}
// 重新设置类名
div.className = classNames.join(" ");

2. 进阶的操作(建议使用)

       className 是一个新的集合类型DOMToken的实例,有length属性,还有以下方法:

  • add(value):相类名列表中添加指定的类名(字符串)value,若该值已经存在,则什么也不做
  • contains(value):返回布尔值,表示给定的value是否存在
  • remove(value):从类名列表中删除指定的字符串值value
  • toggle(value):若类名列表中已经存在指定的value,则删除,若不存在则添加

5.2 焦点管理

5.2.1 document.activeElement

       document.activeElement 始终包含当前拥有焦点的DOM元素。页面加载时,可以通过用户输入(按Tab键或代码中使用focus())让某个元素自动获得焦点

       默认情况下,document.activeElement 在页面刚加载完之后会设置为document.body.而在也米娜完全加载之前,document.activeElement 值为null

5.2.2 document.hasFocus

返回布尔值,表示文档是否拥有焦点

5.3 HTMLDocument 扩展

1. readyState 属性

document.readyState属性的两个值:

  • loading:表示文档正在加载
  • complete:表示文档加载完成

2. compatMode 属性

       指示浏览器当前处于什么渲染模式。标准模式下,document.compatMode的值是”CSS1Compat“,混则模式下,document.compatMode 的值是”BackCompat“

3. head 属性

HTML5新增document.head 属性,指向<head>

5.5 自定义数据属性

见2.4.4

5.6 插入标记

1. innerHTML 属性

       读取innerHTML 时,会返回元素所有后代的HTML字符串,包括元素、注释和文本节点。(包括html标签,同时保留空格和换行)

       写入innerHTML时,则会根据提供的字符串值以新的DOM子树代替元素中原来包含的所有节点。如果赋值不包含任何HTML标签,则直接生成一个文本节点

       实际返回的文本内容会因浏览器而不同,IE 和 Opera 会把所有的元素百年前转换为大写,而 Safari、Chrome 和Firefox 会按照源代码的格式返回

       尽管innerHTML不会执行自己创建的<script>,但仍向恶意用户暴露了很大的攻击面,如果页面中要使用用户提供的信息,建议不要使用innerHTML,以防XSS攻击。

三种动态创建元素的区别

  • document.write()
  • element.innerHTML
  • document.createElement()

区别:

  1. document.write() :是将内容直接写入页面的内容流。但是当文档流执行完毕,它会导致页面全部重绘

  2. innerHTML :是将内容写入某个DOM节点,不会导致页面全部重绘。创建多个元素效率更高,不要拼接字符串,采取数组形式拼接,结构稍微复杂

    function foo(){
          
          
        var d1 = +new Date();
        var arrry = [];
        for(var i=0;i<1000;i++) {
          
          
            arrry.push('<div style="width:100px;height:2px;border:1px solid blue;"></div>')
        }
        document.body.innerHTML = arrry.join('');
        var d2 = +new Date();
        console.log(d2-d1);
    }
    foo();
    
  3. document.createElement():创建多个元素效率稍微低一点点,但结构更清晰

    function foo(){
          
          
        var d1 = +new Date();
        var arrry = [];
        for(var i=0;i<1000;i++) {
          
          
            var div = document.createElement('div');
            div.style.width = '100px';
            div.style.height = '2px';
            div.style.border = '1px solid red';
            document.body.appendChild(div);
        }
        var d2 = +new Date();
        console.log(d2-d1);
    }
    foo();
    

2. outerHTML 属性

       读取outerHTML 属性时,会返回调用它的元素(其本身及所有后代元素)的HTML字符串。

       写入outerHTML 时,调用他的元素会被传入HTML字符串经过解释后生成的DOM子数取代

3. insertAdjcentHTML()与insertAdjacentText()

       这两个方法都接收两个参数:要插入标记的位置和插入的HTML或文本。

       第一个参数必须为下列值中的一个(这些值不区分大小写):

  • ”beforebegin“:插入当前元素的前面,作为前一个同胞节点
  • ”afterbegin“:插入当前元素的内部,作为新的子节点或放在第一个子节点的前面
  • ”beforeend“:插入当前元素的内部,作为新的子节点或放在最后一个子节点的后面
  • ”afterend“:插入当前元素后面,作为下一个同胞节点

        第二个参数会作为HTML字符串解析(与innerHTML 和outerHTML相同)或作为纯文本解析(与innerText 和 outerHTML相同)

5.7 scrollIntoView()

可以滚动浏览器窗口或容器元素以便包含元素的元素进入视口,参数如下:

  • alignToTop(布尔值):
    • true:窗口滚动后元素的顶部与视口顶部对齐
    • false:窗口滚动后元素的底部与视口底部对齐
  • scrollIntoViewOptions是一个选择对象
    • behavior:定义过渡动画,可取的值为”smooth“和”auto“,默认为”auto“
    • block:定义垂直方向的对齐,可取”start“(默认)、”center“、”end“ 和 ”nearest“
    • inline:定义水平方向的对齐,可取”start“、”center“、”end“ 和 ”nearest“(默认)
  • 不传参数等同于alignToTop 为 true

六、专有扩展

       指的是还未被标准化(进入HTML5),各个浏览器厂商为弥补功能缺失而为DOM添加的专有扩展

6.1 children属性

       children 属性是一个HTMLCollection,只包含元素的Element类型的子节点

6.2 contains()

       确定一个元素是否是另一个元素的后代,

       DOM Level 3 的compareDocumentPosition()也可以确定节点间的关系。该方法会返回表示两个节点关系的位掩码

掩码 节点关系
0x1 断开(传入的节点不在文档中)
0x2 领先(传入的节点在DOM树中位于参考节点之前)
0x4 随后(传入的节点在DOM树中位于参考节点之后)
0x8 包含(传入的节点是参考节点的祖先)
0x10 被包含(传入的节点是参考节点的后代)

6.3 插入标记

1. innerText

        对应元素中包含的所有文本内容。读取值时,innerText会按照深度优先顺序将子数中所有的文本的值拼接起来。写入值时,innerText会移除元素的所有后代并插入一个包含该值的文本节点。(去除html标签,同时空格和换行也会去掉)

div.innerText = 'Hello world!';
<div id='content'>Hello world!</div>
  • 设置innerText会移除元素之前的所有后代节点,完全改变DOM子树
  • 设置innerText会编码出现在字符串中的HTML语法字符(大小于号、引号及和号)
  • 通过将innerText设置等于innerText,可以去除所有的HTML标签而只剩下文本

InnerText 和 InnerHtml的不同

innerText:

  • 不识别 html 标签
  • 可获取元素里面的内容
  • 会去除空格和换行

innerHtml:

  • 识别 html 标签
  • 可获取元素里面的内容
  • 保留空格和换行

2. outerText

       作用范围包含调用它的节点

       读取文本时,outerText 与 innerText 实际返回同样的内容。

       写入文本值时,outerText不止会移除所有的后代节点,而是会替换整个元素

       

七、DOM2 和 DOM3

7.1 DOM的演进

       DOM2 和 DOM3 的core模块的目标:扩展DOM API,满足XML的所有需求并提供更好的错误处理和特性检测。

       DOM2 core未新增任何类型,仅仅在DOM1 core 的基础上增加了一些方法和属性。

       DOM 3 core则除了增强原有类型,也新增了新类型

       

7.1.1 XML命名空间

       XML命名空间可以实现在一个格式规范的文档中混用不同的XML语言,而不必担心元素命名冲突。严格来讲,XML命名空间在XHTML中才支持,HTML并不支持。

       XML命名空间是使用xmlns指定的。XHTML的命名空间是”http://www.w3.org/1999/xhtml“,应该包含在任何格式规范的XHTML页面的<html>中:

<html xmlns="http://www/w3.org/1999/xhtml" lang="en">
    <head>
        <title>Document</title>
    </head>
    <body>
        hello!
    </body>
</html>

       对上面的例子来说,所有元素都默认属于XHTML命名空间。可以使用xmlns给命名空间创建一个前缀,格式为:”xmlns:前缀“。
       若文档中只使用一种XML语言,那命名空间前缀其实是多余的,只有一个文档混合使用多种XML语言时才有必要。如下面的文档就使用了XHTML和SVG两种语言:

<html xmlns="http://www/w3.org/1999/xhtml" lang="en">
    <head>
        <title>Document</title>
    </head>
    <body>
        <svg xmlns="http://www.w3/org/2000/svg" version="1.1">
            <rect x="0" y="0" width="100" style="fill: red;"/>
        </svg>
    </body>
</html>

       

1. Node 的变化

DOM2中,Node类型包含以下特定于命名空间的属性:

  • localName:不包含命名空间前缀的节点名
  • namespcaeURI:节点的命名空间URL,未指定则为null
  • prefix:命名空间前缀,未指定则为null

DOM3增加了如下的方法:

  • isDefaultNamespace(namespaceURI):返回布尔值,表示namespaceURI是否为节点的默认命名空间
  • lookupNamespaceURI(prefix):返回给定prefix的命名空间URI
  • lookupPrefix(namespaceURI):返回给定namespURI的前缀

       

2. Document的变化

DOM2 在document类型上新增了如下的命名空间特定方法:

  • createElementNS(namespaceURI,tagName):以给定的标签名tagName创建指定命名空间namespaceURI的一个新元素
  • createAttibuteNS(namespaceURI,attibuteName):以给定的属性名attibuteName创建指定命名空间namespaceURI的一个新属性。
  • getElementByTagNameNS(namespaceURI,attibuteName):返回指定命名空间namespaceURI中所有标签名为tagName的元素的NodeList

       

3. Element的变化

  • getAttributeNS
  • getAttributeNodeNS
  • getElementByTagNameNS
  • hasAttributeNS
  • removeAttributeNS
  • setAttributeNS
  • setAttributeNodeNS

       

7.1.2 其他变化

1. DocumentType的变化

        DocumentType 新增了3个属性:publicId、systemId和internalSubset

       

2. Document的变化

  • importNode:从其他文档获取一个节点并导入新文档。
  • createDocumentType():创建DocumentType类型的节点
  • createDocument():创建新文档
  • createHTMLDocument():创建完整的HTML文档

       

3. Node 的变化:

DOM3 新增了两个用于比较节点的方法:isSameNode()和isEqualNode():

  • isEqualNode():节点相同,即引用同一个对象
  • isSameNode():节点相等,即节点类型相同,拥有相等的属性,且attributes和childNodes也相等

       DOM3 也增加了给DOM节点附加额外数据的方法:setUserData()。 该方法接收3个参数:键、值、处理函数。处理函数会在包含数据的节点被赋值、删除、重命名或导入其他文档的时候执行

       

4. 内嵌窗格的变化

       DOM2 给HTMLIFrameElement <iframe>新增属性:contentDocument。该属性包含代表子内嵌窗格中内容的document 对象的指针。

       还有个属性contentWindow,返回相应的窗格的window对象,有一个document对象

       

7.2 样式

三种定义样式的方法:

  • 外部样式表(<link>
  • 文档样式表(<style>
  • 元素特定样式(style属性)

       

7.2.1 存取元素样式

       CSS属性名使用连字符表示法,在JS中这些属性必须转换为驼峰大小写形式,(注意:float不能转换,其是JS中的保留字):

background-image -------> style.backgroundImage

1. DOM样式属性和方法

  • cssText:包含style属性中的css代码

  • length:应用给元素的CSS属性数量,跟item()一起配套迭代CSS属性

  • parentRule:表示CSS信息的CSSRule对象

  • getPropertyPriority(propertyName):若CSS属性使用了!important,则返回”important“,否则返回空字符串

  • getPropertyValue(propertyName):返回属性propertyName的字符串值

  • item(index):返回索引为index的CSS属性名

    style[i] = style.item(i)

  • removeProperty(propertyName):删除propertyName,使用该方法删除属性意味着会应用该属性的默认样式

  • setProperty(propertyName,value,priority):设置CSS属性propertyName的值为value,priority是”important“空字符串

       

2. 计算样式

       DOM2 Style 在document.defaultView上增加了getComputedStyle()。

document.defaultView.getComputedStyle(myDiv,null);

       该方法接收两个参数:**要取得计算样式的元素和伪元素字符串。**若不需要查询伪元素,则第二个参数可以传null。getComputedStyle()返回一个CSSStyleDeclaration对象,包含元素的计算样式

       注意:在所有浏览器中,计算样式都是只读的,不能修改getComputedStyle()返回的对象。且计算样式还包含浏览器内部样式中的信息。因此有默认值的CSS属性会出现在计算样式中

       

7.2.2 操作样式表

CSSStyleSheet 从 StyleSheet继承的属性:

  • disabled:样式表是否被禁用(可读写)

  • href:是<link>包含的样式表,则返回样式表的URL

  • media:样式表支持的媒体类型集合

  • ownerNode:指向拥有当前样式表的节点,HTML中不是<link>就是<style>。若当前样式表通过@import被包含在另一个样式表中,则该属性值为null

  • parentStyleSheet:若当前样式表通过@import被包含在另一个样式表中,则该属性指向导入他的样式表

  • title:ownerNode的title属性

  • type:字符串,表示样式表的类型。CSS样式表就是”text/css“

CSSStyleSheet 还支持的属性和方法:

  • cssRules:当前样式表包含的样式规则的集合
  • ownerRule:若样式表是使用@import 导入的,则指向导入规则。否则为null
  • deleteRule(index):在指定位置删除cssRules中的规则
  • insertRule(rule,index):在指定位置向cssRules中插入规则

       document.styleSheet 表示文档中可用的样式表集合。这个集合的length属性保存着文档中样式表的数量,而每个样式表都可以使用中括号或item()获取

       

7.2.3 元素尺寸

1. 偏移尺寸

       偏移尺寸包含元素在屏幕上占用的所有视觉空间。视觉空间由高度宽度,包括**所有的内边距,滚动条和边框(不八行外边距)**组成。可用于取得元素偏移尺寸的4个属性:

  • offsetHeight:元素在垂直方向上占用的像素尺寸,包括他的高度、水平滚动条的高度和上下边框的高度
  • offsetLeft:元素左边框外侧距离包含元素左边框内测的像素数
  • offsetTop:元素上边框外侧距离包含元素上边框内测的像素数
  • offsetWidth:元素在水平方向上占用的像素尺寸
  • offsetParent:返回作为该元素带有定位的父级元素,若父级元素没有定位则返回body

注意:

  1. offsetLeft 和 offsetTop是相对于包含元素的
  2. 返回的数值都不带单位

 
       要确定一个元素在页面中的偏移量,可以把它的offsetLeft 和offsetTop属性分别与offsetParent的相同属性相加,一直到根元素

function getElementLeft(element) {
    
    
    let actualLeft = element.offsetLeft;
    let current = element.offsetParent;

    while (current != null) {
    
    
        actualLeft += current.offsetLeft;
        current = current.offsetParent;
    }
    return actualLeft;
}
style 和 offset 的区别

在这里插入图片描述

       

2. 客户端尺寸

       客户端尺寸包含元素内容及其内边距所占用的空间,属性有:clientWidth 和 clientHeight。客户端尺寸实际就是元素内部的空间,因此不包含滚动条占用的空间。这两个属性常用于确认浏览器视口尺寸

       

3. 滚动尺寸

提供元素内容滚动距离的信息,属性有:

  • scrollHeight:没有滚动条出现时,元素内容的总高度
  • scrollWidth:没有滚动条出现时,元素内容的总宽度
  • scrollLeft:内容区左侧隐藏的像素数,设置这个属性可以改变元素的滚动位置,默认为0
  • scrollTop:内容区顶部隐藏的像素数,设置这个属性可以改变元素的滚动位置,默认为0

4. 三大尺寸总结

三大尺寸大小对比 作用
offsetWidth 返回自身包括padding,内容区的宽度,边框,返回数值不带单位
clientWidth 返回自身包括padding,内容区的宽度,不含边框,返回数值不带单位
scrollWidth 返回自身实际宽度,不含边框,返回数值不带单位

主要用法:

  1. offset系列经常用于获得元素位置 offsetLeft offsetTop
  2. client系列经常用于获取元素大小 clientWidth clientHeight
  3. scroll 经常用于获取滚动距离
  4. 主要页面滚动距离通过window.pageXOffset获得
           

5. 确定元素尺寸

       浏览器在每个元素上都暴露了getBoundingClientRect(),返回一个DOMRect对象,包含6个属性:left、top、right、bottom、height和width

       

7.3 遍历

       用于辅助顺序遍历DOM结构的两个类型,从某个起点开始执行对DOM结构的深度优先遍历:NodeIterator 和 TreeWalker

       

7.3.1 NodeIterator

通过document.createNodeIterator () 创建实例,接收4个参数:

  • root:作为遍历根节点的节点

  • whatToShow:数值代码,表示应该访问哪些节点,是个位掩码,通过引用一个或多个过滤器来指定访问哪些节点

  • filter,NodeFilter对象或函数:表示是否接收或跳过特定节点,节点的过滤器函数

    NodeFilter对象只有一个方法accpectNode(),若给定节点应该访问就返回NodeFIlter.FILTER_ACCEPT,否则返回NodeFIlter.FILTER_SKIP

  • entityReferenceExpansion:布尔值,表示是否扩展实体引用

例子:定义只接收<p>元素的接待你过滤器对象:

let filter = {
    
    
    acceptNode(node) {
    
    
        return node.tagName.toLowerCase() == "p"? NodeFilter.FILTER_ACCEPT:NodeFilter.FILTER_SKIP;
    }
};
//filter还可以是函数,下面的写法与上面的等价
let filter = function(node){
    
    
    return node.tagName.toLowerCase() == "p"? NodeFilter.FILTER_ACCEPT:NodeFilter.FILTER_SKIP;
}

let iterator = document.createNodeIterator(root,NodeFilter.SHOW_ELEMENT,filter,false);

       

nextNode()和 previousNode()

NodeIterator 的主要方法,其区别:

  • nextNode():在DOM子树中以深度优先方式进前一步,第一次调用返回根节点。遍历到DOM树最后一个节点时,返回null
  • previousNode():在遍历中后退一步。遍历到DOM树最后一个节点时,返回遍历的根节点

例子:遍历div中所有元素

<div id="div1">
    <p><b>HELLO</b> WORLD!</p>
    <ul>
        <li>List item 1</li>
        <li>List item 2</li>
        <li>List item 3</li>
    </ul>
</div>
let div = document.getElementById("div1");
let iterator = document.createNodeIterator(div,NodeFilter.SHOW_ELEMENT,null,false);
let node = iterator.nextNode();
while(node!=null){
    
    
    console.log(node.tagName);
    node = iterator.nextNode();
}

       

7.3.2 TreeWalker

       TreeWalker 是 NodeIterator 的高级版,用document.createTreeWalker() 创建,与NodeIterator 类似,通常可以取代NodeIterator 。

TreeWalker 的filter有三个返回值:

  • NodeFilter.FILTER_ACCEPT
  • NodeFilter.FILTER_SKIP:跳过节点,访问子树中下一个节点
  • NodeFilter.FILTER_REJECT:表示跳过该节点以及该节点的整个子树

        除了nextNode()和 previousNode(),还添加了parentNode(),firstChild()、lastChild()、nextSibling()和 previousSibling()

       TreeWalker 类型有一个currentNode属性,表示遍历过程中上一次返回的节点。可以通过修改该属性来影响接下来遍历的起点

       

7.3.3 元素遍历

DOM 元素有5个属性:

  • childElementCount:返回子元素数量(不包含文本节点和注释)
  • firstElementChild:指向第一个Element类型的子元素(firstElementChild版:firstChild)
  • lastElementChild:指向最后一个Element类型的子元素(firstElementChild版:lastChild)
  • previousElementSibling:指向前一个Element类型的同胞元素(firstElementChild版:previousSibling)
  • nextElementSibling:指向后一个Element类型的同胞元素(firstElementChild版:nextSibling)

firstElementChild 和 firstChild 的区别:

  • firstElementChild 一定返回的是元素节点
  • firstChild返回的不一定是元素节点

例子:遍历特定元素的所有子元素

let parentElement = document.getElementById('parent');
let currentChildElement = parentElement.firstElementChild;

while(currentChildElement) {
    
    
    // 获得元素节点
    processChild(currentChildElement);
    if(currentChildElement == parentElement.lastChild) {
    
    
        break;
    }
    currentChildElement = currentChildElement.nextElementSibling;
}

       

7.4 DOM范围

       document.createRange(),可以创建一个DOM范围对象,这个新创建的范围对象是与创建它的文档相关联的,不能在其他文档中使用。相关的方法和属性:

  • startContainer:范围起点所在的节点(选区中第一个子节点的父节点)
  • startOffset:范围起点在startContainer中的偏移量。若startContainer为文本节点、注释节点或CData区块,则startOffset指范围起点之气那跳过的字符数;否则,表示范围中第一个节点的索引。
  • endContainer:范围终点所在的节点(选区中最后一个节点的父节点)
  • endOffset:范围起点在startContainer中的偏移量。
  • commonAncestorContainer:文档以startContainer和endContainer为后代的最深节点。

       

7.5 选择

7.5.1 简单选择

       selectNode()和selectNodeContents():通过范围选择文档中某个部分。两个方法都接收一个节点作为参数,并将该节点的信息添加到调用它的范围。selectNode()选择整个节点,包括其后代节点,而selectNodeContents()只选择节点的后代

<p id="p1"><b>hello</b> world!</p>
let range1 = document.createRange(),
    range2 = document.createRange(),
    p1 = document.getElementById("p1");
range1.selectNode(p1);
range2.selectNodeContents(p1);

 
更精细控制范围的方法:

  • setStartBefore(refNode)
  • setStartAfter(refNode)
  • setEndBefore(refNode)
  • setEndAfter(refNode)

       

7.5.2 复杂选择

       setStart() 和 setEnd() 。对setStart(),参照节点会成为startContainer,偏移量赋给startOffset。而setEnd()而言,参照节点会成为endContainer,偏移量会付给endOffset

       

八、DOM操作总结

关于DOM操作,主要是创建、增、删、改、查、属性操作、事件操作

(一)创建

  • document.write
  • innerHTML
  • createElement

(二)增

  • appenChild
  • insertBefore

(三)删

  • removeChild

(四)改

  • 修改元素的属性:src、href、title等
  • 修改普通元素的内容:innerHTML、innerText
  • 修改表单元素:value、type、disabled等
  • 修改元素样式:style、className

(五)查

  • DOM提供的API方法:getElementById、getElementsByTagName 古老方法 不推荐
  • H5提供的新方法:querySelector、querySelectorAll 提倡
  • 利用节点获取元素:父(parentNode)、子(children)、兄(previousElementSibling、nextElementSibiling) 提倡

(六)属性操作

主要针对于自定义属性

  • setAttribute:设置DOM属性值
  • getAttibute:获取DOM属性值
  • removeAttribute :移除属性值

(七)事件操作

猜你喜欢

转载自blog.csdn.net/weixin_45950819/article/details/120636274