十七

作者: xpwei | 来源:发表于2018-04-16 15:58 被阅读19次

    DOM扩展
    对DOM的两个主要的扩展是Selectors API(选择符API)和HTML5。

    选择符API
    Selectors API是由W3C发起指定的一个标准,致力于让浏览器原生支持CSS查询。所有实现这一功能的JavaScript库都会写一个基础的CSS解析器,然后再使用已有的DOM方法查询文档并找到匹配的节点。
    Selectors API Level 1的核心是两个方法:querySelector()和querySelectorAll()。在兼容的浏览器中,可以通过Document和Element类型的实例调用它们。目前已完全支持Selectors API Level 1的浏览器有IE 8+..Firefox 3.5+..Safari 3.1+..Chrome .. Opera 10+。

    querySelector()方法
    querySelector()方法接收一个CSS选择器,返回与该模式匹配的第一个元素,如果没有找到匹配的元素,返回null:

    //取得body元素
    var body = document.querySelector("body");
    //取得ID 为"myDiv"的元素
    var myDiv = document.querySelector("#myDiv");
    //取得类为"selected"的第一个元素
    var selected = document.querySelector(".selected");
    //取得类为"button"的第一个图像元素
    var img = document.body.querySelector("img.button");
    

    通过Document类型调用querySelector()方法时,会在文档元素的范围内查找匹配的元素。而通过Element类型调用querySelector()方法时,只会在该元素后代元素的范围内查找匹配的元素。
    CSS选择符可以简单也可以复杂,视情况而定。如果传入了不被支持的选择符,querySelector()会抛出错误。

    querySelectorAll()方法
    querySelectorAll()方法接收的参数与querySelector()方法一样,都是一个CSS选择符,但返回的是所有匹配的元素而不仅仅是一个元素。这个方法返回的是一个NodeList的实例。
    能够调用querySelectorAll()方法的类型包括Document、
    DocumentFragment 和 Element:

    //取得某<div>中的所有<em>类似于getElementsByTagName("em")
    var ems = document.getElementById("myDiv").querySelectorAll("em");
    //取得类为"selected"的所有元素
    var selecteds = document.querySelectorAll(".selected");
    //取得所有<p>元素中的所有<strong>元素
    var strongs = document.querySelectorAll("p strong");
    
    var i, len, strong;
    for (i = 0, len = strongs.length; i < len; i++) {
        strong = strongs[i]; //或者 strongs.item(i)
        strong.className = "important";
    }
    

    matchesSelector()
    Selectors API Level 2规范为Element类型新增了一个方法matchesSelector()。这个方法接收一个参数,即CSS选择符,如果调用元素与该元素符匹配,返回true;否则false。

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

    在取得某个元素引用的情况下,使用这个方法能够方便地检测它是否会被querySelector()或querySelectorAll()方法返回。
    截止2011年年中,还没有浏览器支持matchesSelector()方法;不过,也有一些实验性的实现。如果想使用这个方法,最好是写一个包装函数:

    function matchesSelector(element, selector) {
        if (element.matchesSelector) {
            return element.matchesSelector(selector);
        } else if (element.msMatchesSelector) {
            return element.msMatchesSelector(selector);
        } else if (element.mozMatchesSelector) {
            return element.mozMatchesSelector(selector);
        } else if (element.webkitMatchesSelector) {
            return element.webkitMatchesSelector(selector);
        } else {
            throw new Error("Not supported.");
        }
    }
    if (matchesSelector(document.body, "body.page1")) {
        //执行操作
    }
    

    元素遍历
    对于元素间的空格,IE9及之前版本不会返回文本节点,而其他所有浏览器都会返回文本节点。这样,就导致了在使用childNodes和firstChild等属性时的行为不一致。为了弥补这一差异,而同时又保持DOM规范不变,Element Traversal规范新定义了一组属性。

    • childElementCount:返回子元素(不包括文本节点和注释)的个数。
    • firstElementChild:指向第一个子元素;firstChild的元素版。
    • lastElementChild:指向最后一个子元素;lastChild的元素版。
    • previousElementSibling:指向前一个同辈元素;previousSibling的元素版。
    • nextElementSibling:指向后一个同辈元素;nextSibling的元素版。
    var i,
        len,
        child = element.firstChild;
    while (child != element.lastChild) {
        if (child.nodeType == 1) { //检查是不是元素
            processChild(child);
        }
        child = child.nextSibling;
    }
    

    而使用Element Traversal新增的元素,代码会更简洁。

    var i,
        len,
        child = element.firstElementChild;
    while (child != element.lastElementChild) {
        processChild(child); //已知其是元素
        child = child.nextElementSibling;
    }
    

    支持Element Traversal规范的浏览器有IE 9+􀇋Firefox 3.5+􀇋Safari 4+􀇋Chrome 􀖖 Opera 10+。

    HTML5
    与类相关的扩充
    1、getElementsByClassName()方法

    //取有所有类中包含"username"和"current"的元素,类名的先后顺序无所谓
    var allCurrentUsernames = document.getElementsByClassName("username current");
    //取得ID 为"myDiv"的元素中带有类名"selected"的所有元素
    var selected = document.getElementById("myDiv").getElementsByClassName("selected");
    

    IE 9+..Firefox 3+..Safari 3.1+..Chrome ..Opera 9.5+。

    2、classList属性

    <div class="bd user disabled">...</div>
    //删除"user"类名
    //首先,取得类名字符串并拆分成数组
    var classNames = div.className.split(/\s+/);
    //找到要删除的类名
    var pos = -1,
        i,
        len;
    for (i = 0, len = classNames.length; i < len; i++) {
        if (classNames[i] == "user") {
            pos = i;
            break;
        }
    }
    //删除类名
    classNames.splice(i, 1);
    //把剩下的类名拼接字符串并重新设置
    div.className = classNames.join(" ");
    

    HTML5新增了一种操作类名的方式,可以让操作更简单也更安全,上面那么多行代码用下面一行代码就可以代替了:

    div.classList.remove("user");
    
    ///删除“disabled”类
    div.classList.remove("disabled");
    //添加"current"类
    div.classList.add("current");
    //切换"user"类
    div.classList.toggle("user");
    //确定元素中是否包含既定的类名
    if (div.classList.contains("bd") && !div.classList.contains("disabled")) {
        //执行操作
    )
    //迭代类名
    for (var i = 0, len = div.classList.length; i < len; i++) {
        doSomething(div.classList[i]);
    }
    

    有Firefox 3.6+􀖖 Chrome。

    焦点管理
    HTML5也添加了辅助管理DOM焦点的功能。首先就是document.activeElement属性,这个属性始终会引用DOM中当前获得了焦点的元素。元素获得焦点的方式有页面加载、用户输入(通常是通过按Tab键)和在代码中调用focus()方法:

    var button = document.getElementById("myButton");
    button.focus();
    alert(document.activeElement === button); //true
    

    默认情况下,文档刚刚加载完成时,document.activeElement中保存的是document.body元素的引用。文档加载期间,document.activeElement的值为null。
    另外就是新增了document.hasFocus()方法,这个方法用于确定文档是否获得了焦点:

    var button = document.getElementById("myButton");
    button.focus();
    alert(document.hasFocus()); //true
    

    通过检测文档是否获得了焦点,可以知道用户是不是正在与页面交互。
    IE 4+..Firefox 3+..Safari 4+..Chrome .. Opera 8+。

    HTMLDocument的变化
    1、readyState属性

    • loading,正在加载文档;
    • complete,已经加载完文档。
      使用document.readyState的最恰当方式,就是通过它来实现一个指示文档已经加载完成的指示器。在这个属性得到广泛支持之前,要实现这样一个指示器,必须借助onload事件处理程序设置一个标签,表明文档已经加载完毕。
    if (document.readyState == "complete"){
        //执行操作
    }
    

    IE4+..Firefox 3.6+..Safari..Chrome .. Opera 9+。

    2、兼容模式
    自从IE6开始区分渲染页面的模式是标准的还是混杂的,检测页面的兼容模式就称为浏览器的必要功能。IE为此给document添加了一个名为compatMode的属性。在标准模式下,document.compatMode的值等于“CSS1Compat”,而在混杂模式下,document.compatMode的值等于“BackCompat”。

    if (document.compatMode == "CSS1Compat") {
        alert("Standards mode");
    } else {
        alert("Quirks mode");
    }
    

    后来,陆续实现这个属性的浏览器有Firefox􀇋Safari 3.1+􀇋Opera 􀖖 Chrome。

    3、head属性
    作为对document.body引用文档的<body>元素的补充,HTML5新增了document.head属性,引用文档的<head>元素。要引用文档的<head>元素,可以结合使用这个属性和另一种后备方法。

    var head = document.head || document.getElementsByTagName("head")[0];
    

    Chrome 􀖖 Safari 5

    字符集属性

    alert(document.charset); //"UTF-16"
    document.charset = "UTF-8";
    
    if (document.charset != document.defaultCharset){
      alert("Custom character set being used.");
    }
    

    支持document.charset属性的浏览器有IE、Firefox、Safari、Opera和Chrome。支持document.defaultCharset属性的浏览器有IE、Safari和Chrome。

    自定义数据属性
    HTML5规定可以为元素添加非标准的属性,但要添加前缀data-,目的是为元素提供与渲染无关的信息,或者提供语义信息:

    <div id="myDiv" data-appId="12345" data-myname="Nicholas"></div>
    var div = document.getElementById("myDiv");
    //取得自定义属性的值
    var appId = div.dataset.appId;
    var myName = div.dataset.myname;
    //设置值
    div.dataset.appId = 23456;
    div.dataset.myname = "Michael";
    //有没有"myname"值呢?
    if (div.dataset.myname){
        alert("Hello, " + div.dataset.myname);
    }
    

    插入标记
    1、innerHTML属性

    div.innerHTML = "Hello world!";
    

    为innerHTML设置的包含HTML的字符串值与解析后innerHTML的值大不相同:

    div.innerHTML = "Hello & welcome, <b>\"reader\"!</b>";
    

    以上操作得到的结果:

    <div id="content">Hello &amp; welcome, <b>&quot;reader&quot;!</b></div>
    

    2、outerHTML属性
    在读模式下,outerHTML返回调用它的元素及所有子节点的HTML标签。在写模式下,outerHTML会根据指定的HTML字符串创建新的DOM子树,然后用这个DOM子树完全替换调用元素:

    div.outerHTML = "<p>This is a paragraph.</p>";
    

    这行代码完成的操作与下面这些DOM脚本代码一样:

    var p = document.createElement("p");
    p.appendChild(document.createTextNode("This is a paragraph."));
    div.parentNode.replaceChild(p, div);
    

    IE4+..Safari 4+..Chrome .. Opera 8+。Firefox7及之前版本都不支持outerHTML属性。

    3、insertAdjacentHTML()方法

    //作为前一个同辈元素插入
    element.insertAdjacentHTML("beforebegin", "<p>Hello world!</p>");
    //作为第一个子元素插入
    element.insertAdjacentHTML("afterbegin", "<p>Hello world!</p>");
    //作为最后一个子元素插入
    element.insertAdjacentHTML("beforeend", "<p>Hello world!</p>");
    //作为后一个同辈元素插入
    element.insertAdjacentHTML("afterend", "<p>Hello world!</p>");
    

    IE􀇋Firefox 8+􀇋Safari􀇋Opera 􀖖 Chrome

    4、内存和性能问题
    在使用innerHTML、outerHTML属性和insertAdjacentHTML()方法时,最好先手工删除要被替换的元素的所有事件处理程序和JavaScript对象属性。使用这几个属性,特别是innerHTML,仍然还是可以为我们提供很多便利。一般来说,在插入大量新HTML标记时,使用innerHTML属性与通过多次DOM操作先创建节点指定它们之间的关系相比,效率要高得多。这是因为在设置innerHTML或outerHTML时,就会创建一个HTML解析器。这个解析器是在浏览器级别的代码(通常是C++)基础上运行的,因此比执行JavaScript快的多。不可避免地,创建和销毁HTML解析器也会带来性能损失,所以最好能够将设置innerHTML或outerHTML的次数控制在合理的范围内:

    var itemsHtml = "";
    for (var i=0, len=values.length; i < len; i++){
        itemsHtml += "<li>" + values[i] + "</li>";
    }
    ul.innerHTML = itemsHtml;
    

    这个例子的效率要高得多,因为它只对innerHTML执行了一次赋值操作。

    scrollIntoView()
    该方法可以在所有HTML元素上调用。如果传入true作为参数,或者不传,那么窗口滚动之后会让调用元素的顶部与视口顶部尽可能平齐。如果传入false,调用元素会尽可能全部出现在视口:

    //让元素可见
    document.forms[0].scrollIntoView();
    

    children属性
    由于IE9之前的版本与其他浏览器在处理文本节点中的空白符时有差异,因此就出现了children属性。

    var childCount = element.children.length;
    var firstChild = element.children[0];
    

    contains()方法

    alert(document.documentElement.contains(document.body)); //true
    

    IE􀇋Firefox 9+􀇋Safari􀇋Opera 􀖖 Chrome

    插入文本
    1、innerText属性

    div.innerText = "Hello world!";
    

    2、outerText属性
    在读取文本值时,outerText与innerText的结果完全一样。但在写模式下,outerText就完全不同了:outerText不只是替换调用它的元素的子节点,而且会替换整个元素:

    div.outerText = "Hello world!";
    

    这行代码实际上相当于如下两行代码:

    var text = document.createTextNode("Hello world!");
    div.parentNode.replaceChild(text, div);
    

    IE4+􀇋Safari 3+􀇋Opera 8+􀖖 Chrome

    相关文章

      网友评论

        本文标题:十七

        本文链接:https://www.haomeiwen.com/subject/jhdskftx.html