从没有父项的insertAdjacentHTML文本获取顶级DOM节点

时间:2019-08-20 21:03:52

标签: javascript html

假设我有:

<body>
  <div class="root">
    <div class="text1"></div>
    <div class="text2"></div>
  </div>
</body>

并想在text2之后插入一些HTML。

var targetHandle = document.querySelector('.root')
targetHandle.insertAdjacentHTML( 'beforeEnd', `
  <div class="text3"></div>
`);

我们可以通过两种方式处理该问题:

var targetHandle = document.querySelector('.root')
var newNode1 = targetHandle.lastElementChild
var newNode2 = document.querySelector('.root > .text3')

到目前为止,一切都很好。但是,如果insertAdjacentHTML的内容很大并且包含许多节点:

var targetHandle = document.querySelector('.root')
targetHandle.insertAdjacentHTML( 'beforeEnd', `
  <div class="text3"></div>
  <div class="text4"></div>
  <div class="text5"></div>
`);

使用document.querySelector成为查找每个顶级节点的真正负担,并且lastElementChild选择器将仅指向要注入的最后一个元素。

您可能会说,这太愚蠢了,只需将这些DOM节点与父对象包装在一起,以便您可以查询和遍历:

var targetHandle = document.querySelector('.root')
targetHandle.insertAdjacentHTML( 'beforeEnd', `
  <div class="parentToTheRescue">
    <div class="text3"></div>
    <div class="text4"></div>
    <div class="text5"></div>
  </div>
`);

虽然这是找到这些节点的有效方法,但我们通过创建HTML作为获取它们的方法对我们的解决方案做出了妥协。

我真正想要的是一种获得每个insertAdjacentHTML注入的顶级节点的句柄的方法,而又无需使用父包装器的妥协:

var nodeList = [ ... ]

结合我的想法,到目前为止我还不疯狂,因为我担心性能问题:

  • 始终使用父包装器。插入后,将获取子节点的句柄。将孩子复制/粘贴到父母级别,删除父母。

  • 为要粘贴到的树中的现有节点拍摄快照。插入后,拍摄另一个快照并进行比较以找到新的节点。

正在寻找其他想法!谢谢

1 个答案:

答案 0 :(得分:2)

const targetHandle = document.querySelector('.root');
const markup = `
  <div class="text3"></div>
  <div class="text4"></div>
  <div class="text5"></div>
`;
const elems = getInsertedElementsFromMarkup( targetHandle, markup, 'beforeend' );
elems.forEach( (elem) => {
  elem.textContent = 'I just got inserted in the doc';
});

function getInsertedElementsFromMarkup( target, markup, position = "beforeend" ) {
  if( !(target instanceof Node) ) {
    throw new TypeError( 'Argument 1 is not a Node' );
  }
  if( typeof markup !== "string" ) {
    throw new TypeError( 'Argument 2 is not a DOMString' );
  }
  
  // Convert our markup to a DocumentFragment
  const frag = target.ownerDocument.createRange()
    .createContextualFragment( markup );

  // Convert to Array,
  // DocumentFragments don't support `:scope` and HTMLCollection is live...
  const elems = [... frag.children];

  switch ( position ) {
    case 'beforebegin':
      target.parentNode.insertBefore( frag, target );
      break;
    case 'afterbegin':
      target.insertBefore( frag, target.firstChild );
      break;
    case 'afterend':
      target.parentNode.insertBefore( frag, target.nextSibling );
      break;
    default: // "beforeend"
      target.appendChild( frag );
  }

  // return our queried nodes, once they are in the doc
  return elems;
}
.root>div {
  border: 1px solid;
  height: 18px;
  margin: 6px 0;
}
<div class="root">
  <div class="text1"></div>
  <div class="text2"></div>
</div>