当前位置: 首页 > Web前端 > HTML

DOM高级工程师不完全指南

时间:2023-04-02 14:37:22 HTML

本文干货部分翻译自:UsetheDOMlikeaPro译者:kyrieliu(刘凯丽)《前端框架香到不敢撕DOM裸手!”虽然大多数前端er都有这样的烦恼,但是本着基础的原则,手撕DOM应该是前端攻城狮的必备技能。这就是本文的初衷——DOM并没有那么难处理,如果你能充分利用它,那么你离爱上它就不远了。三年前刚入前端坑的时候,发现了一个叫jQuery的宝贝。她有一个神奇的$函数,可以让我快速选中某个DOM元素或者一组DOM元素,并提供链式调用减少代码冗余。保持。虽然现在提到jQuery这个词,但你会觉得老土,“都9102年了,你还跟我说诺基亚?”。骨灰归灰,却也真香。尽管近年来Vue和React的崛起加剧了jQuery的衰落,但全球仍有超过6600万个网站在使用jQuery,占全球所有网站的74%。jQuery也给业界留下了影响深远的“遗产”。W3C在$函数之后实现了querySelector和querySelectorAll。具有讽刺意味的是,正是这两个原生方法的出现大大加速了jQuery的衰落,因为它们取代了前者最常用的功能——快速选择DOM元素。这两个新方法虽然写起来有点长(问题不大,封装一下即可),但确实很好用。来吧,走吧!获取DOM元素获取单个元素将任何有效的css选择器传递给document.querySelector以选择单个DOM元素:document.querySelector('.element')document.querySelector('#element')document.querySelector('div')document.querySelector('[name="username"]')document.querySelector('div+p>span')如果页面上没有指定元素,返回null获取元素集合使用document.querySelectorAll获取一个元素的集合,其参数与document.querySelector相同。undefinedundefined大哥jQuery可以简化为:$('body').append('Homepage')但是,伙计们,原生JavaScript也可以做到现在这有效:document.body.insertAdjacentHTML('beforeend','Home')此方法允许您将任何有效的HTML字符串插入到四个由该方法的第一个参数指定的DOM元素的位置是:'beforebegin':在元素之前'afterbegin':在元素内部,在第一个现有子元素之前'beforeend':在元素内部,在最后存在的子元素“afterend”:

很舒服。更舒服的是,它还有两个好兄弟,可以让开发者快速插入HTML元素和字符串://插入HTML元素document.body.insertAdjacentElement('beforeend',document.createElement('a'))//插入文本文档.body.insertAdjacentText('afterbegin','cool!')移动DOM元素上面提到的兄弟方法insertAdjacentElement也可以用来移动已有的元素,换句话说:当传入这个方法的时候文档中已经存在一个元素,元素只是简单地移动(而不是复制和移动)。如果你有下面的HTML:

Title

Subtitle

然后做,把

放在

后面:consth1=document.querySelector('h1')consth2=document.querySelector('h2')h1.insertAdjacentElement('afterend',h2)所以我们得到这样的结果:

Title

Subtitle

替换DOM元素replaceChild?这是几年前做的。每当开发者需要替换两个DOM元素时,除了获取必要的两个元素外,还需要获取它们的直接父元素:parentNode.replaceChild(newNode,oldNode)and现在,开发者可以使用replaceWith来完成两个元素之间的替换:oldElement.replaceWith(newElement)在用法上比前者更干净。需要注意的是,如果传入的newElement在文档中已经存在,则该方法的执行结果将是移动newElement并替换oldElement。如果传入的newElement是字符串,则将其作为TextNode来替换原来的移除DOM元素的旧方法与替换元素相同。旧的移除元素的方法还需要获取目标元素的直接父元素:consttarget=document.querySelector('#target')target.parentNode.removeChild(target)现在只需要执行一次remove方法在目标元素上:consttarget=document.querySelector('#target')target.remove()使用HTML字符串创建DOM元素。insertedAdjacent方法允许开发人员直接将一段HTML插入到文档中。如果我们只想生成一个DOM元素以供将来使用怎么办?DOMParser对象的parseFromString方法可以满足这个要求。该方法可以将一串HTML或XML字符串转换成一个完整的DOM文档,即当我们需要获取期望的DOM元素时,需要从该方法返回的DOM文档中获取该元素:constcreateSingleElement=(domString)=>{constparser=newDOMParser()returnparser.parseFromString(domString,'text/html').body.firstChild}//usageconstelement=createSingleElement('Home')制作一个DOM检查器标准的DOMAPI为开发者提供了许多方便的方法来检查DOM。undefined//还是用上面的例子container.compareDocumentPosition(h1)//20h1.compareDocumentPosition(container)//10h1.compareDocumentPosition(h2)//4h2.compareDocumentPosition(h1)//2个标准语句:element.compareDocumentPosition(otherElement)返回值定义如下:1:两个元素不在同一个文档中2:otherElement在元素之前4:otherElement在元素之后8:otherElement包含元素16:otherElement被元素包含那么问题来了,为什么第一个第一行的结果是20,第二行的结果是10?因为h1同时满足“被容器包含(16)”和“容器后面”,所以该语句的执行结果为16+4=20,同理,第二条语句的结果为8+2=10。DOMObserver:MutationObserver在处理用户交互时,通常当前页面的DOM元素会发生较大的变化,有些场景需要开发者监听这些变化,并在触发后进行相应的操作。MutationObserver是浏??览器提供的监控DOM变化的接口。它的功能强大到足以观察到一个元素几乎所有的变化。可观察的对象包括:文本的变化、子节点的增减,以及任何元素属性的变化。和往常一样,如果要构造任何对象,那么new它的构造函数:constobserver=newMutationObserver(callback)构造函数中传入一个回调函数,当被监控的DOM元素发生变化时,回调函数就会被执行,它的两个参数是:列出包含这次所有更改和观察者本身的MutationRecords。其中,MutationRecords的每一项都是一个变化记录,是一个普通对象,包含以下公共属性:type:变化的类型,attributes/characterData/childListtarget:发生变化的DOM元素elements:移除子元素组成的NodeListattributeName:值发生变化的属性名称,如果不是属性变化则返回nullpreviousSibling:添加或移除子元素之前的兄弟节点nextSibling:添加或移除子元素之后element根据目前的信息,可以写一个回调函数:constcallback=(mutationRecords,observer)=>{mutationRecords.forEach({type,target,attributeName,oldValue,addedNodes,removedNodes,}=>{switch(type){case'attributes':console.log(`attribute${attributeName}changed`)console.log(`previousvalue:${oldValue}`)console.log(`currentvalue:${target.getAttribute(attributeName)}`)breakcase'childList':console.log('childnodeschanged')console.log('added:${addedNodes}')console.log('removed:${removedNodes}')break//...}})}到目前为止,我们有一个DOMobserver观察者,和一个完全可用的undefined配置对象支持以下字段:attributes:Boolean,是否监听元素属性的变化attributeFilter:String[],需要监听的具体属性名的数组attributeOldValue:Boolean,是否记录并传递属性被监控元素变化的前值characterData:Boolean,是否监控目标元素或子元素树中节点包含的字符数据的变化数据变化childList:Boolean,是否监控目标元素添加或删除子元素subtree:Boolean,是否将监控范围扩展到目标元素下整个子树的所有元素。当不再监听目标元素的变化时,调用观察者的disconnect方法即可。如果有必要,可以先调用观察者的takeRecords方法从观察者的通知队列中移除所有待处理的通知,并将它们返回到一个MutationRecord对象数组中:constmutationRecords=observer.takeRecords()callback(mutationRecords)observer.disconnect()不要'不要害怕DOM虽然大多数DOMAPI的名称很长(而且写起来很麻烦),但它们非常强大和通用。这些API通常旨在为开发人员提供底层构建块,以在其上构建更通用、更简洁的抽象逻辑。因此,从这个角度来说,他们必须提供一个完整的名称,才能变得足够清晰和明确。只要能发挥这些API的潜力,为什么不多使用几次按键呢?DOM是每个JavaScript开发人员的必备知识,因为我们几乎每天都在使用它。不要害怕,大胆激发你操作DOM的洪荒之力,早日成为高级DOM工程师。最后扫码捕捉一个有趣的前端er