6

before(),after(),prepend(),append()等新DOM方法简介

 3 years ago
source link: https://www.zhangxinxu.com/wordpress/2017/09/js-dom-before-after-replacewith-append-prepend/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

before(),after(),prepend(),append()等新DOM方法简介

这篇文章发布于 2017年09月28日,星期四,01:07,归类于 JS API。 阅读 29770 次, 今日 42 次 16 条评论

by zhangxinxu from http://www.zhangxinxu.com/wordpress/?p=6443
本文可全文转载,但需得到原作者书面许可,同时保留原作者和出处,摘要引流则随意。

一、DOM API也在不断升级

DOM标准以及更新

web前端标准一直在不断升级,比方说,说了很多年的HTML5、CSS3,以及天天见的ES6。

然后,似乎就没有然后了。实际上,除了HTML5/CSS3/ES6+,关于DOM标准也是在不断升级进步,而且浏览器也在悄悄地进行跟进与支持。

然而,这种跟进与支持呢非常的低调与隐讳;加上行业的话语权大部分集中在JS工程师身上,同时DOM这种东西比较“低级”撑不起场面,因此各种什么技术大会啊,分享会议啊绝对不会讲这玩意儿的。

于是都叫他注意力很容易被带走,而没有意识到在DOM操作这一块,现在实际上已经非常厉害了。

二、before(),after(),prepend(),append()等新增DOM API

接下来要介绍这些新增的DOM API方法,都比较新,其设计目的是可以像jQuery那样,使用非常简单的api,便利的操作dom元素。

这些api包括:before()after()replaceWith()append()prepend(),好,下面一个一个介绍。

1. DOM API之before()

这里的before()是个ChildNode方法,也就是节点方法。节点方法对应于元素方法。区别在于,节点可以直接是文本节点,甚至注释等。但是,元素必须要有HTML标签。

因此,before()的参数既可以是DOM元素,也可以是DOM节点,甚至可以直接字符内容,咦,感觉活脱脱的jQuery的before() API嘛?没错,真的很类似似,并且语义也是一样的,表示当前节点的前面是XXX。

语法如下:

void ChildNode.before((节点或字符串)... 其它更多节点);

从语法可以看出before()方法支持多个参数,并且参数可以是节点对象,也可以是字符串对象。

至于具体细节,我们可以通过一个一个例子来实际验证。

先来看一个最简单例子,已知HTML如下:

<img id="img" src="mm0.jpg">

如果我们希望在图片<img>前面插入写文字,直接可以:

document.getElementById('img').before('美女:');

效果会是这样:

图片前面before()美女文字

如果我们插入的是一段HTML字符串,那效果又是怎样的呢?如下:

document.getElementById('img').before('<strong>美女:</strong>');

结果,当当当当,HTML被转义成了安全的普通文本显示了,如下:

HTML字符串被文本化处理

可以看出原生DOM的before() API和jQuery中的before() API还是有差别的,在jQuery中,before()的参数值是作为html字符处理的,但是这里的before()是作为text字符处理的。

如果我们想要在图片前面插入HTML内容,可以使用DOM节点方式插入,例如:

var eleTitle = document.createElement('h4');
eleTitle.innerHTML = '美女:';
document.getElementById('img').before(eleTitle);

则会表现如下:

brfore()插入h4标题后效果截图

可能有人要疑问了,我非要在图片前面插入HTML字符内容怎么办?

可以使用兼容性更好的insertAdjacentHTML() DOM方法,使用示意如下:

document.getElementById('img').insertAdjacentHTML('beforebegin', '<strong>美女:</strong>');

语法如下:

element.insertAdjacentHTML(position, html);

非本文重点就不展开了。

元素DOM的before() API还有一个很棒的特性,那就是可以同时插入多个节点内容,例如:

document.getElementById('img').before('1. 美女', ' ', '2. 美女');

效果如下截图所示:

同时插入多个节点内容截图示意

demo演示
以上所有出现的before()效果都可以狠狠地点击这里体验:DOM before()节点API方法demo

兼容性
before() API Chrome54+,Firefox49+才支持,还算比较新,IE edge目前还未支持,具体参见下面截图(实时兼容数据):

before()等属性兼容性截图

对于团队或公司内部的一些项目,管理平台或者工具之类的web页面我们可以放心大胆使用before()等API,如果是面向用户对兼容性有要求的项目呢?

很简单,加一段polyfill JS代码就可以了,如下(参考自MDN):

// 源自 https://github.com/jserz/js_piece/blob/master/DOM/ChildNode/before()/before().md
(function (arr) {
  arr.forEach(function (item) {
    if (item.hasOwnProperty('before')) {
      return;
    }
    Object.defineProperty(item, 'before', {
      configurable: true,
      enumerable: true,
      writable: true,
      value: function before() {
        var argArr = Array.prototype.slice.call(arguments),
          docFrag = document.createDocumentFragment();
        
        argArr.forEach(function (argItem) {
          var isNode = argItem instanceof Node;
          docFrag.appendChild(isNode ? argItem : document.createTextNode(String(argItem)));
        });
        
        this.parentNode.insertBefore(docFrag, this);
      }
    });
  });
})([Element.prototype, CharacterData.prototype, DocumentType.prototype]);

注意,上面的polyfill 并不支持IE8及其以下浏览器。也就是before() API只能在至少兼容到IE9浏览器的项目使用。

和insertBefore()比较
insertBefore()作为老牌传统的API,优势在于兼容性好。不足之处,其语法着实很奇怪,A元素插到B元素前面,需要父元素parentNode.insertBefore(newNode, referenceNode),小辈之间的打打闹闹牵扯到父辈,显然事情就会麻烦。至少这么多年下来insertBefore的参数究竟是新插入节点在前还是先插入节点在后,我都没有准确记清楚。

但是,before() API就不一样了,语法仅涉及到插入节点和相对节点,非常好记,不容易出错,而且API名称更短。

2. DOM API之after()

after()before()的语法特性兼容性都是一一对应的,差别就在于语义上,一个是在前面插入,一个是在后面插入。

由于语法类似,因此,就不一个一个示意了,若想直观体验after()的特性表现,您可以狠狠的点击这里:DOM after()节点API方法demo

然后下面的JS代码是对after() API的polyfill,同样地,至少IE9+浏览器。

//源自 https://github.com/jserz/js_piece/blob/master/DOM/ChildNode/after()/after().md
(function (arr) {
  arr.forEach(function (item) {
    if (item.hasOwnProperty('after')) {
      return;
    }
    Object.defineProperty(item, 'after', {
      configurable: true,
      enumerable: true,
      writable: true,
      value: function after() {
        var argArr = Array.prototype.slice.call(arguments),
          docFrag = document.createDocumentFragment();
        
        argArr.forEach(function (argItem) {
          var isNode = argItem instanceof Node;
          docFrag.appendChild(isNode ? argItem : document.createTextNode(String(argItem)));
        });
        
        this.parentNode.insertBefore(docFrag, this.nextSibling);
      }
    });
  });
})([Element.prototype, CharacterData.prototype, DocumentType.prototype]);

3. DOM API之replaceWith()

其语法如下:

ChildNode.replaceWith((节点或字符串)... 更多节点);

表示替换当前节点为其他节点内容。

举个例子,把页面上所有HTML注释都替换成可显示的普通文本节点。

如下JS代码:

var treeWalker = document.createTreeWalker(document.body, NodeFilter.SHOW_COMMENT);    

while (treeWalker.nextNode()) {
    var commentNode = treeWalker.currentNode;
    // 替换注释节点为文本节点
    commentNode.replaceWith(commentNode.data);
}

例如页面上有这么一段注释:

页面上的注释截图

点击某按钮执行上面的JS代码,结果这段注释内容变成普通文本显示在页面上了,效果如下截图:

注释变成普通文本显示截图

眼见为实,您可以狠狠的点击这里:DOM replaceWith()节点API方法demo

对我们开发有没有什么启示呢?比方说页面模板可以放在注释中……

同样,如果对兼容性又要求,可以使用下面的JS polyfill(参考自MDN):

function ReplaceWith(Ele) {
  var parent = this.parentNode,
      i = arguments.length,
      firstIsNode = +(parent && typeof Ele === 'object');
  if (!parent) return;
  
  while (i-- > firstIsNode){
    if (parent && typeof arguments[i] !== 'object'){
      arguments[i] = document.createTextNode(arguments[i]);
    } if (!parent && arguments[i].parentNode){
      arguments[i].parentNode.removeChild(arguments[i]);
      continue;
    }
    parent.insertBefore(this.previousSibling, arguments[i]);
  }
  if (firstIsNode) parent.replaceChild(this, Ele);
}
if (!Element.prototype.replaceWith) {
	Element.prototype.replaceWith = ReplaceWith;
}
    
if (!CharacterData.prototype.replaceWith) {
    CharacterData.prototype.replaceWith = ReplaceWith;
}
if (!DocumentType.prototype.replaceWith) {
    CharacterData.prototype.replaceWith = ReplaceWith;
}

4. DOM API之prepend()

其语法如下:

ParentNode.prepend((节点或字符串)... 更多节点);

表示在当前节点的最前面插入其它节点内容(作为子节点)。其意思和jQuery中的prepend() API是一样的,对jQuery熟悉的人学习这几个API都是分分钟的事情。

参数值特性什么的和before(), after()等方法类似,就不重复展开。

若有兴趣,您可以狠狠地点击这里:DOM prepend()节点API方法demo

对demo因为上的按钮乱点一通之后,会发现所有插入内容都在最前面显示:

prepend API demo页面点击截图示意

在以前要想在元素节点的最前面插入内容,要么使用insertBefore()firstChild,要么使用insertAdjacentHTML()或者insertAdjacentElement()方法,都很啰嗦。

prepend()这个api要更简单和直接。

兼容性和before()一模一样,对于IE9+支持项目,可以辅助下面的polyfill:

// 源自: https://github.com/jserz/js_piece/blob/master/DOM/ParentNode/prepend()/prepend().md
(function (arr) {
  arr.forEach(function (item) {
    if (item.hasOwnProperty('prepend')) {
      return;
    }
    Object.defineProperty(item, 'prepend', {
      configurable: true,
      enumerable: true,
      writable: true,
      value: function prepend() {
        var argArr = Array.prototype.slice.call(arguments),
          docFrag = document.createDocumentFragment();
        
        argArr.forEach(function (argItem) {
          var isNode = argItem instanceof Node;
          docFrag.appendChild(isNode ? argItem : document.createTextNode(String(argItem)));
        });
        
        this.insertBefore(docFrag, this.firstChild);
      }
    });
  });
})([Element.prototype, Document.prototype, DocumentFragment.prototype]);

5. DOM API之append()

其语法如下:

ParentNode.append((节点或字符串)... 更多节点);

表示在当前节点的最后面插入其它节点内容(作为子节点)。其意思和jQuery中的append() API是一样的,细节上就是不支持html字符串直接显示的差别。

您可以狠狠地点击这里:DOM append()节点API方法demo

所有点击按钮插入内容都在图片后面,也就是容器里面的最后显示:
append()  demo页面效果截图

polyfill如下:

// 源自: https://github.com/jserz/js_piece/blob/master/DOM/ParentNode/append()/append().md
(function (arr) {
  arr.forEach(function (item) {
    if (item.hasOwnProperty('append')) {
      return;
    }
    Object.defineProperty(item, 'append', {
      configurable: true,
      enumerable: true,
      writable: true,
      value: function append() {
        var argArr = Array.prototype.slice.call(arguments),
          docFrag = document.createDocumentFragment();
        
        argArr.forEach(function (argItem) {
          var isNode = argItem instanceof Node;
          docFrag.appendChild(isNode ? argItem : document.createTextNode(String(argItem)));
        });
        
        this.appendChild(docFrag);
      }
    });
  });
})([Element.prototype, Document.prototype, DocumentFragment.prototype]);

三、结束语

我们为什么使用jQuery,一是DOM API的便利性,二是内部帮我们搞定了很多兼容性问题,三是友好地扩展和插件机制。

如今,原生的DOM API借鉴jQuery的优点,也整出了很多简单遍历的API方法;如果我们再粘贴一些polyfill JS搞定兼容性问题;再配合ES5很多数据处理方法;DOM leave 3的事件处理等。

基本上就没有需要使用jQuery的理由了,省了资源加载,提高了代码性能,还装了回哔哩哔哩。

所以,基本上,已经不可逆地,在不久将来,不出几年,行业会兴起原生DOM API,原生JS开发前端应用的小风尚,jQuery会越来越不被人提起,完成其历史使命,日后可以领取个终身成就奖。

另外,还有类jQuery的API本文没有介绍,例如Node.remove(),这个API出现相对要早一些,和前面介绍的不是一波出来的,因此没放到一起介绍。

作用如其名,移除当前节点。

好,以上就是本文的全部内容,都是浅薄的知识点。

感谢阅读,欢迎交流!

(本篇完)1f44d.svg 是不是学到了很多?可以分享到微信
1f44a.svg 有话要说?点击这里

本文为原创文章,会经常更新知识点以及修正一些错误,因此转载请保留原出处,方便溯源,避免陈旧错误知识的误导,同时有更好的阅读体验。
本文地址:http://www.zhangxinxu.com/wordpress/?p=6443


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK