我爱编程

十七

2018-04-16  本文已影响19人  xpwei

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规范新定义了一组属性。

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属性

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

上一篇下一篇

猜你喜欢

热点阅读