DOM Extensions(DOM扩展)

简介: 其中 Selectors API Level 1的核心部分定义了两个方法querySelector()、 querySelectorAll(),兼容ie8+ 用在document、Element类型元素上Selectors API Level 2 规范中定义了 matchesSelector()方法,只能用在Element类型的元素上,判断给定的class或者id是否是当前元素上的属性,兼容ie9+,并且各个浏览器兼容的前缀都不一样,见代码中的对该功能的封装

DOM Extensions(DOM扩展)



文章目录

DOM Extensions(DOM扩展)

一、SELECTORS API 新增选择器API

二、ELEMENT TRAVERSAL 元素遍历

三、Html5

1. class类相关的扩充

getElementsByClassName

className属性、 classList属性

2. Focus Management 焦点管理

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

4. 针对字符新增 charset属性

5. 自定义data属性

6.  Markup Insertion插入标记

innerHTML与outerHTML

insertAdjacentHTML

内存和性能问题

7.  scrollIntoView()

四、专有扩展(PROPRIETARY EXTENSIONS)

文档模式(Document Mode)

children属性

`contains()`方法

插入标记( Markup Insertion)

滚动( Scrolling)

五、总结(summary)


一、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返回,而高版本浏览器则会返回空白文本节点,这就导致了使用 childNodes、firstChild时浏览器表现不一致。

为了解决这个问题,而又同时保持现有的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:

兼容所有浏览器


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


6.  Markup Insertion插入标记


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


/*<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定义的 childElementCount、firstElementChild、 lastElementChild、 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规范,另外还有两个实用的专有属性innerText、outerText并未加入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操作的属性和方法,我们可以用这些扩展的属性方法更高效巧妙的实现业务场景下的实际需求。


目录
相关文章
|
7月前
|
XML JavaScript 安全
XML( 可扩展编辑语言 ) —— DOM解析
XML( 可扩展编辑语言 ) —— DOM解析
52 2
|
移动开发 JavaScript 前端开发
|
26天前
|
JavaScript 前端开发
JavaScript操作DOM元素
JavaScript操作DOM元素
11 1
|
1月前
|
JavaScript 前端开发
如何使用 JavaScript 操作 DOM?
如何使用 JavaScript 操作 DOM?
13 0
|
4月前
|
JavaScript 前端开发
JavaScript DOM 操作:什么是事件委托(Event Delegation)?有什么优势?
JavaScript DOM 操作:什么是事件委托(Event Delegation)?有什么优势?
55 1
|
4月前
|
JavaScript 前端开发 安全
JavaScript DOM 操作:解释一下 `innerHTML` 和 `textContent` 的区别。
JavaScript DOM 操作:解释一下 `innerHTML` 和 `textContent` 的区别。
41 1
|
1天前
|
存储 JavaScript 前端开发
JavaScript DOM 操作:解释一下 cookie、sessionStorage 和 localStorage 的区别。
Cookie是服务器发送至客户端的文本信息,会随每个请求发送回服务器,适合控制会话状态但可能暴露隐私。SessionStorage仅在当前会话中存储数据,关闭浏览器后清除,适合临时存储如登录状态。LocalStorage则持久保存数据,即使关闭浏览器也不会清除,适用于存储长期设置。三种方式各有侧重,应按需求选择。
8 0
|
1天前
|
JavaScript 前端开发 安全
JavaScript DOM 操作:解释一下浏览器的同源策略。
**同源策略**是浏览器安全基石,它阻止脚本跨不同协议、域名或端口访问资源,防止恶意行为。例如,HTTP页面无法直接用JS获取HTTPS页面内容。**CORS**允许跨域请求,但需服务器配合设置,通过`document.domain`属性可配置,但仍受限于服务器配置。
9 4
|
15天前
|
JavaScript 前端开发
深入了解 JavaScript 中的 DOM 和 BOM
深入了解 JavaScript 中的 DOM 和 BOM
16 4