当前位置:澳门贵宾厅 > Web > 香到我都不敢徒手撕 www.vip8888.comDOM 了
香到我都不敢徒手撕 www.vip8888.comDOM 了
2020-03-22

不使用深度选择器的话,还有没有其他的方式去修改?

澳门贵宾厅,如同往常一样,如果想构造任何一个对象,那就 new 它的构造函数:

时间: 2019-12-24阅读: 57标签: 用法MutationObserver概览MutationObserver interface可以用来监测DOM树的变化。MutationObserver 是旧的DOM3事件规范Mutation Events特性的一个替换。在DOM事件触发的时候,会触发MutationObserver中传入的callback。DOM监听是不会立刻开始的,必须调用observer()方法才能监听。MutationObserver构造器

三年前我初入前端坑的时候,发现了一个叫做 jQuery 的宝贝,她有一个神奇的 $ 函数,可以让我快速选中某一个或一组 DOM 元素,并提供链式调用以减少代码的冗余。虽然现在提到 jQuery 这个名词,你会觉得老土,“都 9102 年了你跟我说 Nokia?”。土归土,但也是真的香。尽管这几年风生水起的 Vue、React 加剧了 jQuery 的没落,但全世界仍有超过 6600 万个网站在使用 jQuery,占全球所有网站数量的 74%。

监听panel动态插入 .amap-all DOM或许可以试试。

document.querySelector('.element')document.querySelector('#element')document.querySelector('div')document.querySelector('[name="username"]')document.querySelector('div + p  span')

创建并且启动observer

向 document.querySelector 中传入任何有效的 css 选择器,即可选中单个 DOM 元素:

style lang="scss"// 隐藏跳转高德地图的按钮/deep/ .amap-call { display: none;}/style

获取元素集合

function callback(mutationList, observer) { mutationList.forEach((mutation) = { switch (mutation.type) { case 'childList': // 关注mutation.addedNodes和mutation.removedNodes属性 break; case 'attributes': // 关注mutation.target, mutation.attributeName, mutation.oldValue break; } })}
parentNode.replaceChild(newNode, oldNode)

callback接受MutationRecord和MutationObserver两个入参。MutationRecord描述的是变化;MutationObserver触发callback。

于是我们就得到了这样的结果:

实现步骤创建MutationObserver,监听高德地图panel DOM树变化创建callback,捕捉.amap-call class元素隐藏跳转高德地图的按钮组件销毁前disconnect()取消监听代码实现

const mutationRecords = observer.takeRecords()callback(mutationRecords)observer.disconnect()
var observer = new MutationObserver(callback);

www.vip8888.com,那么问题来了,为什么上面例子中第一行的结果是20、第二行的结果是10呢?

原文:

在上面的代码中,我们通过调用观察者对象的 observe 方法,对 id 为 target 的 DOM 元素进行了观测(第一个参数就是需要观测的目标元素),而第二个元素,我们传入了一个配置对象:开启对属性的观测 / 只观测 class 属性 / 属性变化时传递属性旧值 / 开启对子元素列表的观测。

为什么要动态监听panel子节点?全局css会影响污染全局环境,其他地方的高德地图panel会被影响scoped单文件组件中可以使用深度选择器修改样式,/deep/ .amap-all 或者 .map-container .amap-all

上面提到的兄弟方法 insertAdjacentElement 也可以用来对已存在的元素进行移动,换句话说:当传入该方法的是已存在于文档中的元素时,该元素仅仅只会被移动(而不是复制并移动)。

callback() 函数会在observer用observe()开始监视DOM时,指定的观察请求配置相匹配的更改时,将调用callback()函数。所发生的更改(对子列表的更改或对属性的更改)通过查看mutation.type属性。

const createSingleElement = (domString) = { const parser = new DOMParser() return parser.parseFromString(domString, 'text/html').body.firstChild}// usageconst element = createSingleElement('a href="./home"Home/a')
div/div

 // 动态检测panel的amap-call节点observePanelAmapCallNode() { const callback = (mutationList, observer) = { mutationList.forEach((mutation) = { switch (mutation.type) { case 'childList': // 关注mutation.addedNodes和mutation.removedNodes属性 console.log(mutation, observer); if (mutation.addedNodes[0].className === 'amap-call') { mutation.addedNodes[0].style.display = 'none'; } break; default: { console.log(mutation, observer); } } }); }; const targetNode = document.querySelector('#panel'); const observerOptions = { childList: true, subtree: true, }; this.panelAmapCallNodeObserver = new MutationObserver(callback); this.panelAmapCallNodeObserver.observe(targetNode, observerOptions);},// 取消监听observerdisconnectObserver() { this.panelAmapCallNodeObserver.disconnect();},

mounted() { this.observePanelAmapCallNode();}

beforeDestroy() { this.disconnectObserver();}
const target = document.querySelector('#target')target.remove()

下面的代码设置了整个观察进度。

const target = document.querySelector('#target')observer.observe(target, { attributes: true, attributeFilter: ['class'], attributesOldValue: true, childList: true,})

示例下面的例子创建了一个MutationObserver这个observer watch了一个node和它的所有children element的新增和移除,以及element上的attribute的变化。callback function

type: 变更的类型,attributes / characterData / childListtarget: 发生变更的 DOM 元素addedNodes: 新增子元素组成的 NodeListremovedNodes: 已移除子元素组成的的 NodeListattributeName: 值发生改变的属性名,如果不是属性变更,则返回 nullpreviousSibling: 被添加或移除的子元素之前的兄弟节点nextSibling: 被添加或移除的子元素之后的兄弟节点

targetNode上每次元素从DOM树上添加或者删除,callback都会调用;属性值发生变化,callback也会调用。直到disconnect()方法调用,targetNode才会从DOM树上移除。MutationObserver实战自定义高德地图panel样式(与深度选择器效果相同的js方案)

但,各位观众,如今原生 JavaScript 也可以实现这一操作了:

var targetNode = document.querySelector("#someElement");var observerOptions = { childList: true, attributes: true, subtree: true, // 忽略或设置为false,只观察父节点的更改}var observer = new MutationObserver(callback);observer.observe(targetNode, observerOptions);

时间: 2019-12-03阅读: 112标签: dom本文干货部分翻译自:Use the DOM like a Pro译者:kyrieliu(劉凯里)

远古时代,开发者们常用 getElementsByTagName 和 getElementsByClassName 去获取元素集合,但不同于 querySelectorAll,它们获取的是一个动态的 HTMLCollection,这就意味着,它的结果会一直随着 DOM 的改变而改变。

舒服了呀。

oldElement.replaceWith(newElement)

而老大哥 jQuery 可以简化为:

const h1 = document.querySelector('h1')const h2 = document.querySelector('h2')h1.insertAdjacentElement('afterend', h2)

DOMParser 对象的 parseFromString 方法即可满足这样的需求。该方法可以实现将一串 HTML 或 XML 字符串转化为一个完整的 DOM 文档,也就是说,当我们需要获得预期的 DOM 元素时,需要从方法返回的 DOM 文档中获取这个元素:

保护机械键盘,从我做起。

更舒服的是,它还有两个好兄弟,让开发者可以快速地插入 HTML 元素和字符串:

replaceChild? 这是几年前的做法了,每当开发者需要替换两个 DOM 元素,除了需要拿到这必须的两个元素之外,还需要获取他们的直接父元素:

const arr = [...document.querySelectorAll('div')]// orconst alsoArr = Array.from(document.querySelectorAll('div'))

怕啥都不要怕 DOM

const target = document.querySelector('#target')target.parentNode.removeChild(target)
div  h1Title/h1/divdiv  h2Subtitle/h2/div

当不再监听目标元素的变化时,调用 observer 的 disconnect 方法即可,如果需要的话,可以先调用 observer 的 takeRecords 方法从 observer 的通知队列中删除所有待处理的通知,并将它们返回到一个由 MutationRecord 对象组成的数组当中:

// 还是用上面的例子哈container.compareDocumentPosition(h1) // 20h1.compareDocumentPosition(container) // 10h1.compareDocumentPosition(h2) // 4h2.compareDocumentPosition(h1) // 2

元素的局部搜索

如果传入的 newElement 已经存在于文档中,那么方法的执行结果将是 newElement 被移动并替换掉 oldElement如果传入的 newElement 是一个字符串,那么它将作为一个 TextNode 替换掉原有的元素移除 DOM 元素

只要能发挥出这些 API 本应该发挥出的潜能,多敲几下键盘又何妨呢?

constobserver =newMutationObserver(callback)

attributes: Boolean,是否监听元素属性的变化attributeFilter: String[],需要监听的特定属性名称组成的数组attributeOldValue: Boolean,当监听元素的属性发生变化时,是否记录并传递属性的上一个值characterData: Boolean,是否监听目标元素或子元素树中节点所包含的字符数据的变化characterDataOldValue: Boolean,字符数据发生变化时,是否记录并传递其上一个值childList: Boolean,是否监听目标元素添加或删除子元素subtree: Boolean,是否扩展监视范围到目标元素下的整个子树的所有元素

尽管大部分 DOM API 的名字都很长(写起来很麻烦),但它们都是非常强大并且通用的。这些 API 往往旨在为开发者提供底层的构建单元,以便在此之上建立更为通用和简洁的抽象逻辑,因此从这个角度出发,它们必须提供一个完整的名称以变得足够明确和清晰。

那么问题来了,如何将一个伪数组转化为数组呢?ES6 为开发者提供了两个便利的选择:

1: 两个元素不再同一个文档内2: otherElement 在 element 之前4: otherElement 在 element 之后8: otherElement 包含 element16: otherElement 被 element 所包含

const$ =document.querySelector.bind(document)
const link = document.createElement('a')link.setAttribute('href', '/home')link.className = 'active'link.textContent = '首页'// finallydocument.body.appendChild(link)

现在只需要在目标元素上执行一次 remove 方法就 ok 了:

DOM 观察者:MutationObserver

移动 DOM 元素

使用 document.querySelectorAll 可以获取一个元素集合,它的传参和 document.querySelector 一毛一样。它会返回一个静态的 NodeList,如果没有元素被查找到,则会返回一个空的 NodeList 。

返回值定义如下:

因为 h1 同时满足“被 container 所包含(16)” 和 “在 container 之后”,所以语句的执行结果是 16+4=20,同理可推出第二条语句的结果是 8+2=10。

这个方法允许你将任何有效的 HTML 字符串插入到一个 DOM 元素的四个位置,这四个位置由方法的第一个参数指定,分别是:

contains 方法可以检测出一个元素是否包含另一个元素(或者:一个元素是否是另一个元素的子元素):

替换 DOM 元素

当需要查找元素时,不一定每次都基于 document 去查找。开发者可以在任何 HTMLElement 上进行 DOM 元素的局部搜索:

正常情况下,需要写出如下的代码:

虽然这两个新方法写起来有点长(问题不大,封装一哈),但是它们是真的贼好用。

和替换元素的老方法相同,移除元素的老方法同样需要获取到目标元素的直接父元素:

一招鲜:compareDocumentPosition

const callback = (mutationRecords, observer) = { mutationRecords.forEach({ type, target, attributeName, oldValue, addedNodes, removedNodes, } = { switch(type) { case 'attributes': console.log(`attribute ${attributeName} changed`) console.log(`previous value: ${oldValue}`) console.log(`current value: ${target.getAttribite(attributeName)}`) break case 'childList': console.log('child nodes changed') console.log('added: ${addedNodes}') console.log('removed: ${removedNodes}') break // ... } })}
div  h1Title/h1 h2Subtitle/h2/divdiv  /div

但没有一个 API 可以帮助开发者借由子元素向父元素发起查询。

标准语句:

细心的你一定发现了,上文提到的 insertAdjacent 方法允许开发者直接将一段 HTML 插入到文档当中,如果我们此刻只想生成一个 DOM 元素以备将来使用呢?

!-- beforebegin --div !-- afterbegin -- span/span !-- beforeend --/div!-- afterend --

添加 DOM 元素

标准的 DOM API 为开发者们提供了很多便利的方法去检查 DOM 。比如,matches 方法可以判断出一个元素是否匹配一个确定的选择器:

少年,爬上这棵 DOM 树

传入构造函数的是一个回调函数,它会在被监听的 DOM 元素发生改变时执行,它的两个参数分别是:包含本次所有变更的列表 MutationRecords 和 observer 本身。其中,MutationRecords 的每一条都是一个变更记录,它是一个普通的对象,包含如下常用属性:

compareDocumentPosition 是一个强大的 API ,它可以快速判断出两个 DOM 元素的位置关系,诸如:先于、跟随、是否包含。它返回一个整数,代表了两个元素之间的关系。

在处理用户交互的时候,当前页面的 DOM 元素通常会发生很多变化,而有些场景需要开发者们监听这些变化并在触发后执行相应的操作。MutationObserver 是浏览器提供的一个专门用来监听 DOM 变化的接口,它强大到几乎可以观测到一个元素的所有变化,可观测的对象包括:文本的改变、子节点的添加和移除和任何元素属性的变化。

来,冲!

ahref="/home"首页/a

NodeList 是一个可遍历的对象(aka:伪数组),虽然和数组很像,但它确实不是数组,虽然可以利用 forEach 遍历它,但它并不具备数组的一些方法,比如 map、reduce、find。

document.body.insertAdjacentHTML( 'beforeend', 'a href="/home"首页/a')

而如今,开发者们可以使用 replaceWith 就可以完成两个元素之间的替换了:

然后操作一下,把h2搞到h1的后面去:

配置对象支持如下字段:

迷惑之际,MDN 给我提供了一个宝藏方法:closest 。

事实证明,每个优秀的开发者都是很懒的。为了减少对宝贝键盘的损耗,我一般会这么干:

打得字太多了啊喂!

用 HTML 字符串创建 DOM 元素

从用法上来说,要比前者清爽一些。

'beforebegin': 元素之前'afterbegin': 元素内,位于现存的第一个子元素之前'beforeend': 元素内,位于现存的最后一个子元素之后'afterend': 元素之后

需要注意的是:

Starting with the Element itself, the closest() method traverses parents (heading toward the document root) of the Element until it finds a node that matches the provided selectorString. Will return itself or the matching ancestor. If no such element exists, it returns null.

DOM 是每个 JavsScript 开发者必不可少的知识,因为我们几乎每天都在使用它。莫怕,大胆激发自己操作 DOM 的洪荒之力吧,尽早成为一个 DOM 高级工程师。

上述内容的主题是查找 DOM 元素,这是一个自上而下的过程:从父元素向其包含的子元素发起查询。

做一个检查 DOM 的小能手

真的麻烦。

获取 DOM 元素获取单个元素

// 插入 HTML 元素document.body.insertAdjacentElement( 'beforeend', document.createElement('a'))// 插入文本document.body.insertAdjacentText('afterbegin', 'cool!')

jQuery 也给业界留下了产生深远影响的“遗产”,W3C 就仿照其 $ 函数实现了 querySelector 和 querySelectorAll。而讽刺的是,也正是这两个原生方法的出现,大大加快了 jQuery 的没落,因为它们取代了前者最常用的功能 —— 快捷的选择 DOM 元素。

element.compareDocumentPosition(otherElement)
const container = document.querySelector('#container')container.querySelector('#target')

如果你有以下 HTML:

// divh1Title/h1/div// h2Subtitle/h2const $ = document.querySelector.bind(document)const div = $('div')const h1 = $('h1')const h2 = $('h2')div.contains(h1) // truediv.contains(h2) // false

也就是说,closest 方法可以从特定的 HTMLElement 向上发起查询,找到第一个符合指定 css 表达式的父元素(也可以是元素自身),如果找到了文档根节点还没有找到目标时,就会返回 null 。

根据目前的信息,可以写一个 callback 函数了:

// divHello DOM!/divconst div = document.querySelector('div')div.matches('div') // truediv.matches('.say-hi') // truediv.matches('#hi') // false

至此,我们有了一个 DOM 观察者 observer,也有了一个完整可用的 DOM 变化后的回调函数 callback,就差一个需要被观测的 DOM 元素了:

“前端框架真的太香了,香到我都不敢徒手撕 DOM 了!”

如果页面上没有指定的元素时,返回 null

虽然绝大多数前端er都有这样的困扰,但本着基础为大的原则,手撕 DOM 应当是一个前端攻城狮的必备技能,这正是本文诞生的初衷 —— DOM 并没有那么难搞,如果能去充分利用它,那么你离爱上它就不远了。

如果用原生 JavaScript 向 DOM 中添加一个或多个元素,一般开发者的内心都是抗拒的,为啥呢?假设向页面添加一个 a 标签:

$('body').append('a href="/home"首页/a')