DOM Extensions(DOM扩展)

DOM Extensions(DOM扩展)

  • 虽然W3C中的DOM规范已经很完备了,为了浏览器能有更多丰富的功能,各个浏览器都有自己对DOM的扩展,有些扩展大多数浏览器厂商都觉得不错,并实现了他们,这些扩展就已经成为名义上的标准了,W3C便会考虑将他们加入正式规范中。
  • 指定DOM扩展的主要两个标准是 Selectors API and HTML5,其中定义了一些属性和方法,更方便DOM操作,还有 Element Traversal规范中定义了一些属性用于遍历Element节点
  • 虽然很多DOM扩展W3C都出台了相应规范,仍然还有大量的浏览器专有扩展存在,这些专有属性扩展了浏览器的功能,并有望在未来被加入标准规范。

作为开发者,为什么要知道这些DOM扩展呢?-- DOM扩展,扩展了DOM操作的属性和方法,我们可以用这些扩展的属性方法更高效巧妙的实现业务场景下的实际需求。

一、SELECTORS API 新增选择器API

Selectors API(www.w3.org/TR/selectors-api),是由W3C发起的,用于支持浏览器中的CSS查询,

  • 其中 Selectors API Level 1的核心部分定义了两个方法querySelector()querySelectorAll(),兼容ie8+ 用在document、Element类型元素上
  • Selectors API Level 2 规范中定义了 matchesSelector()方法,只能用在Element类型的元素上,判断给定的class或者id是否是当前元素上的属性,兼容ie9+,并且各个浏览器兼容的前缀都不一样,见代码中的对该功能的封装
//get the body element
var body = document.querySelector("body");
//get the element with the ID "myDiv"
var myDiv = document.querySelector("#myDiv");
//get first element with a class of "selected"
var selected = document.querySelector(".selected");
//get first image with class of "button"
var img = document.body.querySelector("img.button");

//get all <em> elements in a <div> (similar to getElementsByTagName("em"))
var ems = document.getElementById("myDiv").querySelectorAll("em");
//get all elements with class of "selected"
var selecteds = document.querySelectorAll(".selected");
//get all <strong> elements inside of <p> elements
var strongs = document.querySelectorAll("p strong");

// 对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")) { // 如果body元素上class属性对应的值中存在page1的class类名,返回true
   //do something
}

二、ELEMENT TRAVERSAL 元素遍历

思考如下代码:

/*<ul id="mylist">
    <li>Item 1</li>
</ul>
*/
var mylist = document.getElementById('mylist');
alert(mylist.childNodes.length); // 3 > ie9+ 其他浏览器 
alert(mylist.childNodes.length); // 1 >  ie8

在ie8中,空白节点不会被当成text node返回,而高版本浏览器则会返回空白文本节点,这就导致了使用 childNodesfirstChild时浏览器表现不一致。
为了解决这个问题,而又同时保持现有的DOM规范, Element Traversal( www.w3.org/TR/ElementTraversal/)定义了一组新的属性,这5个属性如下:
childElementCountfirstElementChildlastElementChildpreviousElementSiblingnextElementSibling
这五个方法都是在 Element元素类型上的操作,所以就不用担心 上述空白文本节点(Text类型)导致的问题啦。

兼容性: Internet Explorer 9+, Firefox 3.5+, Safari 4+, Chrome, andOpera 10+.

三、Html5

1. class类相关的扩充

自从HTML4依赖,class属性被用的越来越多,使得js与css的class类交互增加,包括动态的改变class、给定class查询document,为了适应开发人员,Html5规范加入了一些方法,让css的class类使用更便捷。

getElementsByClassName

兼容 Internet Explorer 9+, Firefox 3+,Safari 3.1+, Chrome, and Opera 9.5+

//get all elements with a class containing “username” and “current”, though it
//doesn’t matter if one is declared before the other
var allCurrentUsernames = document.getElementsByClassName("username current");
//get all elements with a class of “selected” that exist in myDiv’s subtree
var selected = document.getElementById(“myDiv”).getElementsByClassName("selected");

className属性、 classList属性

使用className删除元素的一个属性:(兼容所有浏览器)

/*
<div class=”bd user disabled”>...</div>
*/
//remove the “user” class
//first, get list of class names
var classNames = div.className.split(/\s+/);
//find the class name to remove
var pos = -1,
    i,
    len;
for (i = 0, len = classNames.length; i < len; i++) {
    if (classNames[i] == "user") {
        pos = i;
        break;
    }
}
//remove the class name
classNames.splice(i, 1);
//set back the class name
div.className = classNames.join("");

使用 classList操作元素的class属性值 只兼容到 ie10+

//remove the "disabled" class
div.classList.remove("disabled");
//add the "current" class
div.classList.add("current");
//toggle the "user" class
div.classList.toggle("user");
//figure out what’s on the element now
if (div.classList.contains("bd") && !div.classList.contains("disabled")) {
    //do something
)
//iterate over the class names
for (var i = 0, len = div.classList.length; i < len; i++) {
    doSomething(div.classList[i]);
}

2. Focus Management 焦点管理

H5新增的两个焦点管理属性:
document.activeElement - 当前获得了焦点元素的指针
document.hasFocus() - 是否当前document存在焦点

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

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

支持浏览器 Internet Explorer 4+, Firefox 3+, Safari 4+, Chrome, andOpera 8+

csx.

  • activeElement获取当前获取焦点元素、hasFocus()判断当前文档是否有元素获取焦点(eg,是否有input被获取焦点)
  • document.activeElement默认为document.body,在文档未完全加载前,值为null
  • 用户输入input的焦点获取通常通过tab键或者focus()方法

3. Changes to HTMLDocument(针对document对象新增3个属性)

HtmlDocument继承Document接口, window的全局对象document对象是 HtmlDocument类型的实例,以下是HTML5针对ocument对象扩展3属性,而这些属性可能在某些浏览器中早就已经作为专有扩展而存在了,这一次只是将其正式加入HTML5规范(以便所有浏览器对照规范去实现)

  • document.readyState
    文档状态 loading、 complete
    兼容ie4+
  • document.compatMode
    浏览器模式
    CSS1Compat表示标准模式
    BackCompat表示怪异模式(quirks mode)
    兼容ie6+
  • document.head
    指向head元素的指针
    只有 Chrome and Safari 5实现了该属性

eg:

 if (document.readyState == “complete”){
    //do something
}

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

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

csx.
document对象新增的3个属性:readyState、compatMode、head

4. 针对字符新增 charset属性

document.charset 获取或者设置文档编码

  • 文档编码通常在<meta>标签中被指定,也可以通过document.charset属性设置
    eg:<meta charset="utf-8">
  • 兼容所有浏览器
alert(document.charset); //”UTF-16” 默认
document.charset = “UTF-8”; // 设置编码

csx.
document.charset属性,获取、设置文档编码

5. 自定义data属性

element.dataset 获取、设置元素上data-属性的值,ie11开始完全兼容该属性

//<div id="myDiv" data-csx='001'>xxx</div>
var myDiv = document.getElementById("myDiv");
alert(myDiv.dataset.csx); // 获取属性 001
myDiv.dataset.csx = '002'; // 设置属性
alert(myDiv.dataset.csx); // 002

csx.慎用 element元素的dataset属性获取data-开头的属性的值,因为浏览器兼容

6. Markup Insertion插入标记

插入html元素到document,以前做法是新增一些DOM节点,然后将这些节点按顺序插入DOM树,如下,但是,如果要插入大量的html元素,这样操作就显得冗余笨重,为此,HTML5标准化了下列3个方法(innerHTMLouterHTMLinsertAdjacentHTML),让元素插入更简单快速。

/*<ul id="myList">
    <li>Item 1</li>
    <li>Item 2</li>
    <li>Item 3</li>
</ul>*/
var myList = document.getElementById("myList");
var newLi = document.createElement("li");
newLi.appendChild(document.createTextNode("new item"));
myList.appendChild(newLi);

innerHTML与outerHTML

innerHTML与outerHTML

  • 读取模式下 outerHtml比innerHtml多一层最外面的元素,如下代码
  • innerHtml在插入<script>、<style>时,ie浏览器与别的浏览器表现不一样,要注意兼容性问题
/*<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> */
var div = document.getElementById('content');
console.log("innerHtml-->",div.innerHTML);
console.log("outerHtml-->",div.outerHTML);

/*
innerHtml--> 
        <p>This is a <strong>paragraph</strong> with a list following it.</p>
        <ul id="myList">
            <li>Item 1</li>
            <li>Item 2</li>
            <li>Item 3</li>
        </ul>
    
outerHtml--> <div id="content">
        <p>This is a <strong>paragraph</strong> with a list following it.</p>
        <ul id="myList">
            <li>Item 1</li>
            <li>Item 2</li>
            <li>Item 3</li>
        </ul>
    </div>
*/

insertAdjacentHTML

insertAdjacentHTML 在元素不同的位置插入html元素
兼容ie8+

//insert as previous sibling
element.insertAdjacentHTML("beforebegin", "<p>Hello world!</p>");

//insert as first child
element.insertAdjacentHTML("afterbegin", "<p>Hello world!</p>");

//insert as last child
element.insertAdjacentHTML("beforeend", "<p>Hello world!</p>");

//insert as next sibling
element.insertAdjacentHTML("afterend", "<p>Hello world!</p>");

内存和性能问题

a. 内存问题
在用上述3个方法替换子节点时,如果该子节点上绑定了事件、js对象,这些子节点被移除时,绑定的事件处理、js对象仍在内存中,增加了页面占用的内存,所以在使用innerHtml、outerHtml、insertAdjacentHtml时,最好手工移除元素上的事件处理程序和js对象属性。ie8中增加了该方法window.toStaticHTML()来移除事件,其他浏览器、ie其他版本需要手工移除

var text = "<a href=\"#\" onclick=\"alert(‘hi’)\">Click Me</a>";
var sanitized = window.toStaticHTML(text); //Internet Explorer 8 only
alert(sanitized); //"<a href=\"#\">Click Me</a>"

b. 性能问题
为什么在大量插入html元素时,innerHtml(outerHtml),比那些大量的DOM操作节点的方式要更高效呢?
因为在每一个调用 innerHtml(outerHtml)插入元素时,会创建一个解析器程序去解析要插入的字符串,解析成dom节点插入文档,而该解析器程序是浏览器级别的代码,通常用C++写的,所以比js快
另一方面,这也导致了一些性能开销,所以要限制调用innerHtml/outerHtml的次数

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

// 避免上面的写法,正确写法如下
var itemsHtml = "";
for (var i=0, len=values.length; i < len; i++){
    itemsHtml += "<li>" + values[i] + "</li>";
}
ul.innerHTML = itemsHtml;

7. scrollIntoView()

在各大浏览器的专有方法中,挑选出了 scrollIntoView() 方法加入HTML5规范
scrollIntoView(),将指定元素滚动到视口区域,加了参数true,表示元素的顶部和viewport顶部对齐,另外 focus()获取焦点的方法,也有同样的效果
兼容所有浏览器

 //make sure this element is visible
document.forms[0].scrollIntoView();
// focus也有同样效果
document.forms[0].focus();

四、专有扩展(PROPRIETARY EXTENSIONS)

前面介绍了,各个浏览器专有的DOM扩展是为了丰富自己浏览器的功能,当这些功能被推广开来,其他浏览器也去实现该功能,这些扩展也就成了事实上的标准,虽然他们还未被进入DOM规范,本节介绍的就是这类专有扩展。

文档模式(Document Mode)

IE8引进了文档模式的概念,一句话,就是在不同文档模式下,对CSS、JS的支持程度是不同的。
<meta http-equiv=”X-UA-Compatible” content=”IE=IEVersion”>
可以通过在meta标签中 设置IEVersion字段来改变文档模式
eg:
<meta http-equiv="X-UA-Compatible" content="IE=9">
强制将文档模式切换为ie9标准模式

children属性

  • 跟 Element Traversal API定义的 childElementCountfirstElementChildlastElementChildpreviousElementSiblingnextElementSibling定义的五个属性一样,都是为了解决浏览器将空白字符解析成文本节点的问题,children属性返回当前元素的所有的子节点(only Element类型)
  • 兼容性: Internet Explorer 5(除了ie8版本仍然返回文本节点、注释节点), Firefox 3.5, Safari 2 (buggy), Safari 3(complete), Opera 8, and Chrome (all versions).
/*
<ul id="mylist">
    <li>Item 1</li>
</ul>
*/
var mylist = document.getElementById("mylist");
console.log(mylist.children.length); // 1  不包含空白字符的文本节点了
console.log(mylist.children[0]);

contains()方法

  • nodeA.containers(nodeB) 给定的nodeB节点是否包含在nodeA节点中,兼容 Internet Explorer,Firefox 9+, Safari, Opera, and Chrome
  • 另外一个方法 compareDocumentPosition()( DOM Level 3中的),跟contains功能一样,兼容: Internet Explorer 9+, Firefox,Safari, Opera 9.5+, and Chrome
  • 有个工具代码结合了上述两个方法,保证各个浏览器都能实现判断一个节点是否包含另外一个节点的功能,如下代码:
// contains  判断html元素是否包含body元素
alert(document.documentElement.contains(document.body)); //true,
 
 // compareDocumentPosition 返回值是16位掩码
 var result =document.documentElement.compareDocumentPosition(document.body);
alert(!!(result & 16)); // true

// 兼容各个浏览器的封装代码(主要对Safari低版本做了兼容)
function contains(refNode, otherNode) {
    if (typeof refNode.contains == "function" &&
        (!client.engine.webkit || client.engine.webkit >= 522)) {
        return refNode.contains(otherNode);
    } else if (typeof refNode.compareDocumentPosition == "function") {
        return !!(refNode.compareDocumentPosition(otherNode) & 16);
    } else {
        var node = otherNode.parentNode;
        do {
            if (node === refNode) {
                return true;
            } else {
                node = node.parentNode;
            }
        } while (node !== null);
        return false;
    }
}

插入标记( Markup Insertion)

innerHtml、outerHtml被加入了HTML5规范,另外还有两个实用的专有属性innerTextouterText并未加入H5规范

  • innerText 用于返回当前元素及其所有子节点上的文本,用于赋值时,会用新的文本节点替换原来元素上所有的子节点,兼容: Internet Explorer 4+, Safari 3+, Opera 8+, and Chrome
  • 另外, DOM Level 3中定义的 textContent属性功能与innerText一样,兼容 Internet Explorer9+, Safari 3+, Opera 10+, and Chrome,firefox,可以写一个兼容性封装函数,如下代码区
  • outerText 用法同上,只是outText在获取、赋值时,会把当前元素上的内容一起返回、替换,并不常用
function getInnerText(element) {
    return (typeof element.textContent == "string") ?
        element.textContent : element.innerText;
}

function setInnerText(element, text) {
    if (typeof element.textContent == "string") {
        element.textContent = text;
    } else {
        element.innerText = text;
    }
}
  • 如何利用 innerText移除指定元素的子元素的所有标签:
/*
<div id="div"><span>hello</span><em>world!</em></div>
*/
var div = document.getElementById("div");
div.innerText = div.innerText;  // 这一行代码足矣
console.log(div); // < div id="div">helloworld!</div>

滚动( Scrolling)

scrollIntoView()方法被加入了HTML5规范,所有浏览器都支持该方法,其他各个浏览器中,还有3个滚动的方法,scrollIntoViewIfNeeded(alignCenter)scrollByLines(lineCount)scrollByPages(pageCount)
目前只有chrome、safari实现了这三个方法,我自己测试下来,chrome也不支持,如果支持的话,scrollByPages(-1)向下滚动一屏,这个方法会很有用。

 //scroll body by five lines
document.body.scrollByLines(5);

//make sure this element is visible only if it’s not already
document.images[0].scrollIntoViewIfNeeded();

//scroll the body back up one page
document.body.scrollByPages(-1);

五、总结(summary)

DOM规定了与HTML文档交互的核心API,其他一些规范提供了对标准DOM的扩展,其中很多扩展都是基于各个浏览器专有扩展而发展来的,本文主要涉及了下面三类扩展规范

  • Selectors API:基于css选择器定义了两个查找DOM元素的方法: querySelector() querySelectorAll()
  • Element Traversal:遍历元素的5个方法,能够查找指定元素的各个位置上(元素前后、元素子节点的前后)的元素,这5个方法主要是为了解决childNodes、firstChild
    获取元素的子节点时,会将空白字符解析成文本节点这一个问题
  • HTML5:html5提供了大量的对标准DOM的扩展,包括已经成为各个浏览器事实上的标准的方法(如innerHtml)和新增的方法,这些方法用来处理焦点管理、文档加载状态、字符集、自定义元素属性、标记批量插入、滚动等
  • 其他浏览器专有属性。这些专有属性作为各个浏览器的专有属性,有些已经成为事实上的标准,日后可能会纳入规范中。

最后,作为开发者,为什么要知道这些DOM扩展呢?-- DOM扩展,扩展了DOM操作的属性和方法,我们可以用这些扩展的属性方法更高效巧妙的实现业务场景下的实际需求。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 200,841评论 5 472
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,415评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 147,904评论 0 333
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,051评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,055评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,255评论 1 278
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,729评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,377评论 0 255
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,517评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,420评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,467评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,144评论 3 317
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,735评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,812评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,029评论 1 256
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,528评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,126评论 2 341

推荐阅读更多精彩内容

  •   尽管 DOM 作为 API 已经非常完善了,但为了实现更过的功能,仍然会有一些标准或专有的扩展。   2008...
    霜天晓阅读 410评论 0 0
  • 第一部分 HTML&CSS整理答案 1. 什么是HTML5? 答:HTML5是最新的HTML标准。 注意:讲述HT...
    kismetajun阅读 27,381评论 1 45
  •   DOM(文档对象模型)是针对 HTML 和 XML 文档的一个 API(应用程序编程接口)。   DOM 描绘...
    霜天晓阅读 3,603评论 0 7
  •   JavaScript 与 HTML 之间的交互是通过事件实现的。   事件,就是文档或浏览器窗口中发生的一些特...
    霜天晓阅读 3,467评论 1 11
  • 1### Menu 第11 章 DOM 扩展 11.1.1 querySelector()方法 11.1.2 qu...
    ft207741阅读 556评论 0 0