回顾文档对象模型 (DOM)(三)

一、MutationObserver

接口提供了监视对DOM树所做更改的能力。它被设计为旧的Mutation Events功能的替代品,该功能是DOM3 Events规范的一部分。

1、MutationObserverInit

字典描述了 MutationObserver 的配置。因此,它主要被用作 MutationObserver.observe() 方法的参数类型。
当调用 observe() 方法时,childList,attributes 或者 characterData 三个属性之中,至少有一个必须为 true,否则会抛出 TypeError 异常。

(1)attributeFilter

字典的可选属性 attributeFilter 是一个字符串数组,用于指定要监听变化的属性名称。 如果指定了该属性,则 attributes 无论是否为 true,属性监听都将启用。

如果属性 attributes 设置为 true ,但options对象中未包含 attributeFilter ,则所有属性的变化都会被监听。

var options = {
  attributeFilter: [ "list", "of", "attribute", "names" ]
}
function callback(mutationList) {
  mutationList.forEach(function(mutation) {
    switch(mutation.type) {
      case "attributes":
        switch(mutation.attributeName) {
          case "status":
            userStatusChanged(mutation.target.username, mutation.target.status);
            break;
          case "username":
            usernameChanged(mutation.oldValue, mutation.target.username);
            break;
        }
        break;
    }
  });
}

var userListElement = document.querySelector("#userlist");

var observer = new MutationObserver(callback);
observer.observe(userListElement, {
  attributeFilter: [ "status", "username" ],
  attributeOldValue: true,
  subtree: true
});

callback()函数将在监听器启动时传给observe()方法,用来检查MutationRecord对象的每一项。对于代表属性更改的任何项(可以通过MutationRecord.type的值为“ attributes”的值来检测),我们使用通过MutationRecord.attributeName获得的属性名称来识别发生的更改的类型,然后将其分派给适当的处理函数。

请注意,在查找本地用户数组时可以使用MutationRecord.oldValue来获取username的上一个值。

当observe()被调用时, options对象中指定了attributeFilter和subtree,所以id为“userlist”的元素的所有子树的所有节点的属性值都会被监听。attributeOldValue被设置为true,因为我们希望在监听到值变化时可以获取到上一个值。
(2)attributeOldValue
当监视节点的属性改动时,将此属性设为 true 将记录任何有改动的属性的上一个值

(3)attributes 设为 true 以观察受监视元素的属性值变更。默认值为 false。
(4)characterData 设为 true 以监视指定目标节点或子节点树中节点所包含的字符数据的变化。无默认值。
(5)characterDataOldValue 设为 true 以在文本在受监视节点上发生更改时记录节点文本的先前值。
(6)childList 设为 true 以监视目标节点(如果 subtree 为 true,则包含子孙节点)添加或删除新的子节点。默认值为 false。
(7)subtree 设为 true 以将监视范围扩展至目标节点整个节点树中的所有节点。

2、disconnect

方法告诉观察者停止观察变动。 可以通过调用其observe()方法来重用观察者。

如果被观察的元素被从DOM中移除,然后被浏览器的垃圾回收机制释放,此MutationObserver将同样被删除。

3、observe

方法配置了 MutationObserver 对象的回调方法以开始接收与给定选项匹配的DOM变化的通知。根据配置,观察者会观察 DOM 树中的单个 Node,也可能会观察被指定节点的部分或者所有的子孙节点。

mutationObserver.observe(target[, options])

target
DOM树中的一个要观察变化的DOM Node (可能是一个Element) , 或者是被观察的子节点树的根节点。
options 可选
一个可选的MutationObserverInit 对象,此对象的配置项描述了DOM的哪些变化应该提供给当前观察者的callback。
(1)复用 MutationObserver

你可以多次调用同一个 MutationObserver 对象的 observe() 方法,来观察 DOM 树中不同部分的变化,和/或不同类型的变化。有一些需要注意的注意事项:
如果在已经被同一 MutationObserver 观察的节点上调用 observe() 方法,则在激活新观察者之前,所有现有观察者将自动从所有正在观察的目标中移除。
如果同一个 MutationObserver 还没有作用在 target 上,则保留现有观察者并添加新观察者。

(2)当节点断开连接时继续观察节点

MutationObserver 旨在让您能够随着时间的推移观察所需的节点集,即使这些节点之间的直接连接被切断。如果你开始观察节点的子树,并且该子树的一部分被分离并移动到DOM中的其他位置,你将继续观察分离的节点段,接收与节点从原始子树分离之前相同的回调。
换句话说,在你收到有关节点从被观察子树中拆分的通知之前,你将收到有关该拆分子树及其节点的更改的通知。这可以防止你丢失在切断连接之后以及在你有机会专门开始观察已移动的节点或子树之前发生的变化。
这意味着理论上如果你跟踪描述发生的变化的MutationRecord对象,你就可以“撤销”这些改动,将DOM恢复到初始状态。

// 得到要观察的元素
var elementToObserve = document.querySelector("#targetElementId");

// 创建一个叫 `observer` 的新 `MutationObserver` 实例,
// 并将回调函数传给它
var observer = new MutationObserver(function() {
    console.log('callback that runs when observer is triggered');
});

// 在 MutationObserver 实例上调用 `observe` 方法,
// 并将要观察的元素与选项传给此方法
observer.observe(elementToObserve, {subtree: true, childList: true});

4、takeRecords()

方法返回已检测到但尚未由观察者的回调函数处理的所有匹配DOM更改的列表,使变更队列保持为空。 此方法最常见的使用场景是在断开观察者之前立即获取所有未处理的更改记录,以便在停止观察者时可以处理任何未处理的更改。

var targetNode = document.querySelector("#someElement");
var observerOptions = {
  childList: true,
  attributes: true
}

var observer = new MutationObserver(callback);
observer.observe(targetNode, observerOptions);

/* ...later, when it's time to stop observing... */

/* handle any still-pending mutations */

var mutations = observer.takeRecords();

if (mutations) {
  callback(mutations);
}

observer.disconnect();

二、MutationRecord

每个 MutationRecord 都代表一个独立的 DOM 变化,在每次随 DOM 变化调用 MutationObserver 的回调函数时,一个相应的 MutationRecord 会被作为参数,传递给回调函数。

在这里插入图片描述

三、Node

是一个接口,各种类型的 DOM API 对象会从这个接口继承。它允许我们使用相似的方式对待这些不同类型的对象;比如, 继承同一组方法,或者用同样的方式测试。
以下接口都从 Node 继承其方法和属性:
Document, Element, Attr, CharacterData (which Text, Comment, and CDATASection inherit), ProcessingInstruction, DocumentFragment, DocumentType, Notation, Entity, EntityReference

在方法和属性不相关的特定情况下,这些接口可能返回 null。它们可能会抛出异常 - 例如,当将子节点添加到不允许子节点存在的节点时。

function removeAllChildren(element){
  while(element.firstChild){
    element.removeChild(element.firstChild);
  }
}
function eachNode(rootNode, callback){
	if(!callback){
		var nodes = [];
		eachNode(rootNode, function(node){
			nodes.push(node);
		});
		return nodes;
	}

	if(false === callback(rootNode))
		return false;

	if(rootNode.hasChildNodes()){
		var nodes = rootNode.childNodes;
		for(var i = 0, l = nodes.length; i < l; ++i)
			if(false === eachNode(nodes[i], callback))
				return;
	}
}

四、NodeFilter

接口表示一个对象,此对象用于过滤 NodeIterator 或 TreeWalker 中的节点。它既不能处理 DOM,也不能遍历节点;它只能根据提供的过滤器对单个节点进行判定。

NodeFilter.acceptNode()

方法会返回一个无符号短整型,用于表明给出的 Node 是否要被 NodeIterator 或 TreeWalker 的迭代算法所接受。该方法应由 NodeFilter 的使用者重写。可返回的值有:

在这里插入图片描述该函数如需要TreeWalker访问节点则需返回 NodeFilter.FILTER_ACCEPT,如果需要忽略节点及其子节点则需返回NodeFilter.FILTER_REJECT,除此之外还可以返回 NodeFilter.FILTER_SKIP。

var nodeIterator = document.createNodeIterator(
  // 作为搜索起点的根节点
  document.getElementById('someId'),

  // 只需要文本节点
  NodeFilter.SHOW_TEXT,

  // 一个包含用于NodeFilter的accpetNode方法的对象
    { acceptNode: function(node) {
      // 一段用于判明是否需要解释、拒绝或越过节点的逻辑
      // 在本例中,仅需要接受不包含空白内容的节点
      if ( ! /^\s*$/.test(node.data) ) {
        return NodeFilter.FILTER_ACCEPT;
      }
    }
  },
  false
);

// Show the content of every non-empty text node that is a child of root
var node;

while ((node = iterator.nextNode())) {
  alert(node.data);
}

五、NodeIterator

接口表示一个遍历 DOM 子树中节点列表的成员的迭代器。节点将按照文档顺序返回。

const nodeIterator = document.createNodeIterator(
    document.body,
    NodeFilter.SHOW_ELEMENT,
    {
      acceptNode(node) {
        return node.nodeName.toLowerCase() === 'p' ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT;
      }
    }
);
const pars = [];
let currentNode;

while (currentNode = nodeIterator.nextNode()) {
  pars.push(currentNode);
}

六、NodeList

对象是节点的集合,通常是由属性,如Node.childNodes 和 方法,如document.querySelectorAll 返回的。NodeList 不是一个数组,是一个类似数组的对象(Like Array Object)。虽然 NodeList 不是一个数组,但是可以使用 forEach() 来迭代。你还可以使用 Array.from() 将其转换为数组。

var parent = document.getElementById('parent');
var child_nodes = parent.childNodes;
console.log(child_nodes.length); // 我们假设结果会是“2”
parent.appendChild(document.createElement('div'));
console.log(child_nodes.length); // 但此时的输出是“3”

NodeList.length :NodeList 中包含的节点个数。
NodeList.entries(); 该方法返回一个迭代协议,允许遍历此对象中包含的所有键/值。该值也是一个Node 对象。

var node = document.createElement("div"); 
var kid1 = document.createElement("p"); 
var kid2 = document.createTextNode("hey"); 
var kid3 = document.createElement("span"); 
node.appendChild(kid1); 
node.appendChild(kid2); 
node.appendChild(kid3); 

var list = node.childNodes;

// 使用 for..of 循环
for(var entry of list.entries()) { 
  console.log(entry);
}

NodeList.item(); 根据给定的索引,返回一个 NodeList对象中包含的Node对象.

**NodeList.forEach();**方法按插入顺序为列表中的每个值对调用一次参数中给定的回调。

let node = document.createElement("div");
let kid1 = document.createElement("p");
let kid2 = document.createTextNode("hey");
let kid3 = document.createElement("span");

node.appendChild(kid1);
node.appendChild(kid2);
node.appendChild(kid3);

let list = node.childNodes;

list.forEach( 
  function(currentValue, currentIndex, listObj) { 
    console.log(currentValue + ', ' + currentIndex + ', ' + this); 
  },
  'myThisArg'
);

NodeList.keys(); 方法返回 iterator ,此方法允许遍历这个对象中包含的所有的键

var node = document.createElement("div"); 
var kid1 = document.createElement("p"); 
var kid2 = document.createTextNode("hey"); 
var kid3 = document.createElement("span"); 

node.appendChild(kid1); 
node.appendChild(kid2); 
node.appendChild(kid3); 

var list = node.childNodes; 

// Using for..of 
for(var key of list.keys()) { 
   console.log(key); 
}

NodeList.values(); 该方法返回一个iterator迭代器,可以利用迭代器遍历所有value。

var node = document.createElement("div"); 
var kid1 = document.createElement("p"); 
var kid2 = document.createTextNode("hey"); 
var kid3 = document.createElement("span"); 

node.appendChild(kid1); 
node.appendChild(kid2); 
node.appendChild(kid3); 

var list = node.childNodes; 

// Using for..of 
for(var value of list.values()) { 
  console.log(value); 
}

七、ParentNode

ParentNode 混合了所有(拥有子元素的) Node 对象包含的共有方法和属性。
ParentNode 是一个原始接口,不能够创建这种类型的对象;它在 Element、Document 和 DocumentFragment 对象上被实现。

1、属性

ParentNode.childElementCount 只读
返回一个当前 ParentNode 所含有的后代数量。
ParentNode.children 只读
返回一个包含 ParentNode 所有后代 Element 对象的动态 HTMLCollection 对象,忽略所有非元素子节点。
ParentNode.firstElementChild 只读
返回父节点的第一个 Element 后代,没有时返回 null。
ParentNode.lastElementChild 只读
返回父节点的最后一个 Element 后代,没有时返回 null。

2、方法

ParentNode.append()
在父节点 ParentNode 的最后一个后代后面插入一组 Node 对象或 DOMString 对象。DOMString 对象会以同等的 Text 节点插入。
ParentNode.prepend()
在父节点 ParentNode 第一个后代前插入一组 Node 对象或者 DOMString 对象。DOMString 对象会以同等的 Text 节点插入。
ParentNode.querySelector()
返回以当前元素为根元素,匹配给定选择器的第一个元素 Element。
ParentNode.querySelectorAll()
返回一个 NodeList,表示以当前元素为根元素的匹配给定选择器组的元素列表。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值