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个属性如下:
  childElementCount、 firstElementChild、  lastElementChild 、 previousElementSibling、 nextElementSibling
这五个方法都是在 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 获取或者设置文档编码

  • 文档编码通常在标签中被指定,也可以通过document.charset属性设置
    eg:
  • 兼容所有浏览器
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在插入
/*<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=\"#\" οnclick=\"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:
 
强制将文档模式切换为ie9标准模式

children属性

  • 跟 Element Traversal API定义的 childElementCountfirstElementChildlastElementChild、 previousElementSibling、 nextElementSibling定义的五个属性一样,都是为了解决浏览器将空白字符解析成文本节点的问题,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操作的属性和方法,我们可以用这些扩展的属性方法更高效巧妙的实现业务场景下的实际需求。

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值