《JavaScript高级程序设计》- 第十五章:DOM拓展

第十五章:DOM拓展

尽管DOM API已经很不错了,但是仍然不断有新的标准或专有的拓展出现,以此来支持更多的功能;W3C正在着手于指定相关的规范。

在此期间,诞生了几个比较受欢迎的DOM拓展:Selectors、HTML5;另外还有较小的Element Traversal规范。

15.1 Selectors API

Selectors API 是 W3C推荐标准,其规定了浏览器原生支持CSS查询的API;

Selectors API Level 1有两个核心方法:querySelector()querySelectorAll()

Selectors API Level 2在Element类型上,新增了更多方法,如:matches()find()findAll();但并未被浏览器声明许可;

通过浏览器原生支持API,解析和遍历DOM树可以通过底层编译语言实现,使得性能有了数量级的提升

15.1.1 querySelector()

querySelector()方法,接收一个参数:

  • CSS选择符

返回匹配该模式的第一个后代元素;如果不匹配,返回null。

// 获取body元素
let body = document.querySelector('body');
// 获取body元素下的类名为myClass的第一个元素
let myDiv = body.querSelector('.myClass');
// 获取类名为button的图片
let img = document.body.querySelector('img.button');

显然,如果在document上使用,则会从文档开始搜索;如果在元素上使用,则会在该元素的后代中开始搜索。

注意:如果选择符有错误,该方法则会抛出错误;

15.1.2 querySelectorAll()

使用和querySelector()一样,但是返回值有所不同:

  • 如果匹配成功,返回一个NodeList的静态实例;反之返回一个空的NodeList的静态实例。

注意:这里的NodeList实例并不是"实时的",只是当前状态下,查询到结果的一个*“快照”***

querySelector()一样,也会因为选择符错误而抛出错误

15.1.3 matches()

matches()接收一个参数,用于判断是否包含带有该CSS选择符的元素。

  • CSS选择符

如果匹配,则返回true;反之,返回false。

15.2 元素遍历

由于有些浏览器,或不同版本的浏览器,会把元素间空格当成空白节点

W3C通过新的Element Traversal规范,给DOM元素定义一组新的属性:

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

如果不适用Element Traversal规范,遍历子元素的话,可以这样做:

let parentElement = document.getElementById('parent');
let currentChildNode = parentElement.firstChild;

// 一直遍历子元素,直至为null
while( currentChildNode ) {
    if( currentChildNode.nodeType === 1){ // 判断是否为Element类型
        // 若果是,则进行元素操作
    }
    if( currentChildNode === parentElement.lastChild ) {	// 如果是最后一个元素,就退出遍历
        break;
    }
    currentChildNode = currentChildNode.nextSibling;
}

可以使用Element Traversal规范改写

let parentElement = document.getElementById('parent');
let currentChildNode = parentElement.firstChildElement;

// 一直遍历子元素,直至为null
while( currentChildNode ) {
    // 直接对元素进程操作
    // ...
    
    // 判断是否为最后一个元素
    if( currentChildNode === parentElement.lastChildElement ) {
        break;
    }
    currentChildNode = currentChildNode.nextElementSibling;
}

15.3 HTML5

HTML5代表着与以往HTML截然不同的方向;在以往的HTML中,从来没有出现过描述JavaScript接口的情形。关于JavaScript绑定的事,一律交给DOM规范去定义

在HTML5规范中,包含了与标记相关的大量JavaScript API定义

15.3.1 CSS类拓展

自HTML4依赖,页面中最大的变化就是class属性用得越来越多。所以HTML增加了方便使用CSS类的API

  1. getElementsByClassName()方法接收一个参数:

    • 包含一个或多个类名的字符串

    该方法返回类名中,包含相应类的元素的NodeList这个NodeList实例是实时

    let aim = document.getElemenstByClassName('current active');
    // 获取文档中,所有类名中,包含‘current’和‘active’的元素;
    

    注意:必须是类名中,既有current 也有 active的元素。

  2. classList属性

    如果要操作类名,可以通过classList属性来实现添加、删除和替换;

    由于classList属性是一个字符串,所以每次操作后,重新全部设值。(会显得有点麻烦)

    // <div class="current active username"></div>
    // 需要删除username类名
    let targetClass = 'username';
    let classNames = div.className.split(/\s+/);    // 按照任何空白符号分割
    
    // 获取需要删除元素的下标
    let index = classNames.indexOf(targetClass);
    
    if( index > -1) {
        classNames.splice(index,1); //删除该下标的元素
    }
    
    // 重新给div设置类名
    div.className = classNames.join(" ");
    

    与此同时,classList是一个新的集合类型:DOMTokenList的实例,也拥有lengthitem()[]的方法,此外还新增了如下方法:

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

    显然,多了如上方法后,删除类名就方便多了。

    div.remove('username');
    
15.3.2 焦点管理

HTML5新增了辅助DOM焦点管理的功能;

  1. document.activeElement:始终指向当前拥有焦点的DOM元素
    • 在页面完成加载之后,指向document.body
    • 在页面加载完成之前,指向null
  2. element.hasFocus():判断是否获取了焦点
    • 返回布尔值,true代表获取了焦点
let button = document.getElementById('myButton');
button.focus();
console.log(document.activeElement === button);	// true
console.log(button.hasFocus());		// true
15.3. 3 HTMLDocument拓展
  1. document.readyState属性,可能有如下两个值:

    • loading:表示文档正在加载。
    • complete:表示文档加载完成。
    if( document.readyState == 'complete' ){
        // do something
    }
    

    在这个属性之前,开发者通常使用window.onload事件处理程序设置一个标记。

  2. compatMode属性,有如下两个值:

    • CSS1Compat:标准模式渲染;
    • BackCompat:混杂模式渲染。
  3. document.head属性,始终指向<head>元素。

15.3.4 字符集属性

使用characterSet属性,可以获取字符集属性。

15.3.5 自定义数据属性

在HTML5中,允许自定义非标准的属性,但是需要使用前缀data-告诉浏览器。

<div data-name="div1"
        data-myName="div2"
        data-my-name="div3">
        hello world
</div>

定义了自定义数据后,可以通过元素的dataset属性访问;

dataset时一个DOMStringMap实例,包含一组键值对的映射

可以直接通过data-后面的名字来访问

/*
<div
        id="mydiv" 
        data-name="div1"
        data-myName="div2"
        data-my-name="div3">
        hello world
</div>
*/
let div = document.getElementById('mydiv');
console.log(div);
console.log(div.dataset)
console.log(div.dataset.name);		// div1
console.log(div.dataset.myname);	// div2
console.log(div.dataset.myName);	// div3

注意:第一个除外,只要被分割,开头就必需大写!

自定义数据属性非常适合需要给元素附加某些数据的场景, 比如链接追踪和在聚合应用程序中标识页面的不同部分。 另外, 单页应用程序框架也非常多地使用了自定义数据属性。

15.3.6 插入标记

DOM虽然提供了很多操作节点的API,但是一次性插入大量HTML还是比较麻烦的;

HTML5新增了一些插入标记,简化了其操作;

  1. innerHTML属性

    在读取innerHTML属性时,会返回元素所有后代的HTML字符串,包括元素、注释和文本节点;

    在写入innerHTML属性时,会根据提供的字符串,以新的DOM子树代替元素中的所有节点

    /*
    <div id="content">
            <p>This is a <strong>paragraph</strong> with a list following it.</p>
            <ul>
                <li>Item 1</li>
                <li>Item 2</li>
                <li>Item 3</li>
            </ul>
    </div>
    */
    let div = document.getElementById('content');
    console.log(div.innerHTML);
    /* 会输出如下内容
       <p>This is a <strong>paragraph</strong> with a list following it.</p>
       <ul>
            <li>Item 1</li>
            <li>Item 2</li>
            <li>Item 3</li>
       </ul>
    */
    div.innerHTML = '<p>This is a <strong>paragraph</strong> with a list following it.</p>'
    /* 会变成这样
    <div id="content">
           <p>This is a <strong>paragraph</strong> with a list following it.</p>
    </div>
    */
    

    读取innerHTML的值,可能会因为浏览器的不同而有所不同。

  2. innerHTML的注意点:

    在所有现代浏览器中,通过innerHTML插入<script>都是不会执行的;

    在IE8及之前的版本中,通过innerHTML插入非可控标签(<script><style>注释)都是不会执行的;需要在非可控标签前,添加可控标签。

    div.innerHTML = " _ <script defer><\/script>";	// 字符串充当可控标签
    div.innerHTML = "<div>&nbsp</div><script defer><\/script>"; // 充当可控标签
    

    注意:script或style都需要额外添加defer属性

  3. outerHTML属性:

    innerHTML类似,但是outerHTML会返回包含自身在内的HTML字符串;

    /*
    <div id="content">
            <p>This is a <strong>paragraph</strong> with a list following it.</p>
            <ul>
                <li>Item 1</li>
                <li>Item 2</li>
                <li>Item 3</li>
            </ul>
    </div>
    */
    let div = document.getElementById('content');
    console.log(div.innerHTML);
    /* 会输出如下内容
    <div id="content">
            <p>This is a <strong>paragraph</strong> with a list following it.</p>
            <ul>
                <li>Item 1</li>
                <li>Item 2</li>
                <li>Item 3</li>
            </ul>
    </div>
    */
    div.innerHTML = '<p>This is a <strong>paragraph</strong> with a list following it.</p>'
    /* 会变成这样
    	<p>This is a <strong>paragraph</strong> with a list following it.</p>
    */
    
  4. insertAdjacentHTML()

    HTML5新增的方法,接收两个参数

    • 要插入标记的位置
    • 插入的HTML字符串

    而且,第一个参数必需是如下值中的一个:

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

    注意:假设为<p>Hello World</p>;

    beforebegin中的begin指的是:<p>

    beforeend中的end指的是:</p>

  5. 其他方法

    除了上述的三个方法外,其实还有与它们类似的三个方法,只需要把HTML替换成Text即可;

    用法与上述三个方法一致,只是传参的字符串,不会被解析为HTML,只会解析为纯文本。

  6. 内存与性能问题:

    上述介绍的方法,达到替换子节点的功能;但是会存在一定的问题:

    • 原来绑定元素引用的JavaScript对象、事件处理程序还停留在内存中

    如果这种替换操作频繁发生,则会导致内存占用攀升

    最好手动删除被替换元素上的关联程序、对象。

  7. 跨站点脚本

    网站攻击者,容易通过innerHTML注入数据,造成XSS攻击!

15.3.7 scrollIntoView()

该方法存在于HTML元素上,可以滚动浏览器窗口或容器元素,以便包含元素进入视口。

其接收如下参数:

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

15.4 专有拓展

尽管所有浏览器都知道遵循标准的重要性,但是不可避免的,它们也有一些专属的拓展,用于弥补自身功能的缺失

这里就不详细介绍了,有需要可以自行查阅。

15.5 小结

  • Selectors API为基于CSS选择符获取DOM元素定义了几个方法: querySelector()querySelectorAll()matches()
  • Element Traversal在DOM元素上定义了额外的属性, 以方便对DOM元素进行遍历。 这个需求是因浏览器处理元素间空格的差异而产生的。
  • HTML5为标准DOM提供了大量扩展。 其中包括对innerHTML属性等事实标准进行了标准化, 还有焦点管理、 字符集、 滚动等特性。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值