javascript 函数查找多个HTML元素的子元素并用作自定义属性

92dk7w1h  于 2023-09-29  发布在  Java
关注(0)|答案(5)|浏览(119)

因此,我有以下结构,其中包含多个<div>元素,我希望在数据属性(data-children)中显示每个元素中的子元素。而不是手动更改属性,我希望它是动态的JS,所以它的变化时,我添加/删除子元素。

<h2 class="amount_title" data-children="1">
  The amount of children of this element is
</h2>

<div class="parent_container">
  <div class="children_element">
    <div>
      <p>Lorem ipsum dolor sit amet.</p>
    </div>
    <button>Button</button>
  </div>
</div>

<h2 class="amount_title" data-children="2">
  The amount of children of this element is
</h2>

<div class="parent_container">
  <div class="children_element">
    <div>
      <p>Lorem ipsum dolor sit amet.</p>
    </div>
    <button>Button</button>
  </div>
  <div class="children_element">
    <div>
      <p>Lorem ipsum dolor sit amet.</p>
    </div>
    <button>Button</button>
  </div>
</div>

所以现在我正在执行以下操作来获取每个元素的子元素并填充每个标题的data-attribute

const parentContainer = document.querySelectorAll('.parent_container')
const amountTitle = document.querySelectorAll('.amount_title')

let childrenAmount1 = parentContainer[0].childElementCount
amountTitle[0].dataset.children = childrenAmount1

let childrenAmount2 = parentContainer[1].childElementCount
amountTitle[1].dataset.children = childrenAmount2

我有几个这样的,所以它显然不是理想的。哪个函数可以帮助我遍历所有这些元素,并相应地用每个元素的子元素数量填充每个数据集?谢谢
我试着做一个for()循环,但是所有的数据集都从数组的最后一个元素中抓取子元素的数量

46qrfjad

46qrfjad1#

基于这种配置,您的最佳策略是在每个标题之后获取下一个元素。虽然我推荐一个wrapper根据每个wrapper的内容设置标题.wrapper > amount_title|parent_container,先遍历所有wrapper,然后从wrapper引用中获取其子级

const titles = document.querySelectorAll(".amount_title");
titles.forEach((title) => {
  const next = title.nextElementSibling;
  if (!next.classList.contains("parent_container")) { return false; } // killswitch
  title.innerHTML = `The amount of children of this element is ${ next.childElementCount }`;
});
<h2 class="amount_title" data-children="1">The amount of children of this element is </h2>
<div class="parent_container">
    <div class="children_element">
        <div>
            <p>Lorem ipsum dolor sit amet.</p>
        </div>
        <button>Button</button>
    </div>
</div>

<h2 class="amount_title" data-children="2">The amount of children of this element is </h2>
<div class="parent_container">
    <div class="children_element">
        <div>
            <p>Lorem ipsum dolor sit amet.</p>
        </div>
        <button>Button</button>
    </div>
    <div class="children_element">
        <div>
            <p>Lorem ipsum dolor sit amet.</p>
        </div>
        <button>Button</button>
    </div>
</div>
  • 编辑:* Package 方法:

这个方法允许在内部移动任何元素,而不关心元素的顺序。当其他方法工作时,此方法允许您在内容之前或之后设置标题,而不会中断

const wrappers = document.querySelectorAll(".wrapper");
wrappers.forEach((wrapper) => {
  const title = wrapper.querySelector(".amount_title");
  const contents = wrapper.querySelector(".parent_container");
  if (!title || !contents) { return; } // killswitch
  title.innerText = `The amount of children of this element is ${ contents.childElementCount }`
});
<div class="wrapper">
  <h2 class="amount_title" data-children="1">The amount of children of this element is </h2>
  <div class="parent_container">
      <div class="children_element">
          <div>
              <p>Lorem ipsum dolor sit amet.</p>
          </div>
          <button>Button</button>
      </div>
  </div>
</div>

<div class="wrapper">
  <h2 class="amount_title" data-children="2">The amount of children of this element is </h2>
  <div class="parent_container">
      <div class="children_element">
          <div>
              <p>Lorem ipsum dolor sit amet.</p>
          </div>
          <button>Button</button>
      </div>
      <div class="children_element">
          <div>
              <p>Lorem ipsum dolor sit amet.</p>
          </div>
          <button>Button</button>
      </div>
  </div>
</div>
6jygbczu

6jygbczu2#

只需选择所有标题,并使用forEach循环遍历所有这些标题。然后选择下一个同级元素,并计算其中包含类child_element的所有元素:

window.addEventListener('DOMContentLoaded', getAmountTitles);

function getAmountTitles() {
  // selects all h2 headliens with the amount_title class
  const HEADLINES = document.querySelectorAll('h2.amount_title');
  
  // forEach loop to iterate
  HEADLINES.forEach(headline => {
    // selects the enxt Sibling element (parent_container)
    let parent_container = headline.nextElementSibling;
    
    // gets the amount of child elements with the class children_element
    let amountChildElements = countChildrenElements(parent_container);
    
    // adds the data attribtue with the amount
    headline.dataset.children = amountChildElements;
    
    // adds the text to the headlines with the number
    headline.insertAdjacentText('beforeend', amountChildElements);
  });
}

function countChildrenElements(parentElement) {
  return (parentElement.querySelectorAll('.children_element').length);
}
<h2 class="amount_title">The amount of children of this element is </h2>

<div class="parent_container">
  <div class="children_element">
    <div>
      <p>Lorem ipsum dolor sit amet.</p>
    </div>
    <button>Button</button>
  </div>
</div>

<h2 class="amount_title">The amount of children of this element is </h2>

<div class="parent_container">
  <div class="children_element">
    <div>
      <p>Lorem ipsum dolor sit amet.</p>
    </div>
    <button>Button</button>
  </div>
  <div class="children_element">
    <div>
      <p>Lorem ipsum dolor sit amet.</p>
    </div>
    <button>Button</button>
  </div>
</div>
hyrbngr7

hyrbngr73#

如果header(h2.amount_title)总是直接在div.parent_container之前,则可以使用previousElementSibling给予[data-children]赋值。代码段使用css显示子元素的数量。

document.querySelectorAll(`.parent_container`)
  .forEach(container => {
    container.previousElementSibling
      .dataset.children = container.childElementCount;
  });
.amount_title:after {
  content: attr(data-children);
  color: green;
}
<h2 class="amount_title" data-children="1">The amount of children of this element is </h2>
<div class="parent_container">
  <div class="children_element">
    <div>
      <p>Lorem ipsum dolor sit amet.</p>
    </div>
    <button>Button</button>
  </div>
</div>

<h2 class="amount_title" data-children="2">The amount of children of this element is </h2>
<div class="parent_container">
  <div class="children_element">
    <div>
      <p>Lorem ipsum dolor sit amet.</p>
    </div>
    <button>Button</button>
  </div>
  <div class="children_element">
    <div>
      <p>Lorem ipsum dolor sit amet.</p>
    </div>
    <button>Button</button>
  </div>
</div>

h2.amount_titlediv.parent_container之间可能有元素时,最好将元素 Package 在div中。

document.querySelectorAll(`.parent_container`)
  .forEach(container => {
    container.closest(`.item`).querySelector(`.amount_title`)
      .dataset.children = container.childElementCount;
  });
.amount_title:after {
  content: attr(data-children);
  color: green;
}
<div class="item">
  <h2 class="amount_title" data-children="1">The amount of children of this element is </h2>
  <div class="parent_container">
    <div class="children_element">
      <div>
        <p>Lorem ipsum dolor sit amet.</p>
      </div>
      <button>Button</button>
    </div>
  </div>
</div>

<div class="item">
  <h2 class="amount_title" data-children="2">The amount of children of this element is </h2>
  <h3>**Container starts in next div</h3>
  <div class="parent_container">
    <div class="children_element">
      <div>
        <p>Lorem ipsum dolor sit amet.</p>
      </div>
      <button>Button</button>
    </div>
    <div class="children_element">
      <div>
        <p>Lorem ipsum dolor sit amet.</p>
      </div>
      <button>Button</button>
    </div>
  </div>
</div>
rjzwgtxy

rjzwgtxy4#

您可以使用 ishas 选择器以及相邻的兄弟组合符+来获取 .amount_title 元素,这些元素将 .parent_container 作为相邻的兄弟。
然后循环它们以更新它们的data-children属性和textContent。它不会访问任何没有 .parent_container 作为相邻兄弟的 .amount_title 元素,因此.nextElementSibling应该总是返回 .parent_container 元素。

window.onload = () => {
  // Select all .amount_title elements with a following
  // .parent_container sibling
  document.querySelectorAll(':is(.amount_title):has(+ .parent_container)')
    // For each such element, get the next element sibling's count
    // of .children_elements and do updates
    .forEach(h => {
      let count = h.nextElementSibling.querySelectorAll('.children_element').length;
      h.textContent += count;
      h.dataset.children = count;
    }
  );
};

console.log(document.querySelectorAll(':is(.amount_title):has(+ .parent_container)').length);
<h2 class="amount_title" data-children="">The amount of children of this element is </h2>

<div class="parent_container">
  <div class="children_element">
    <div>
      <p>Lorem ipsum dolor sit amet.</p>
    </div>
    <button>Button</button>
  </div>
</div>

<h2 class="amount_title" data-children="">No .parent_container: </h2>

<h2 class="amount_title" data-children="">No .children_element: </h2>
<div class="parent_container"></div>

<h2 class="amount_title" data-children="">The amount of children of this element is </h2>

<div class="parent_container">
  <div class="children_element">
    <div>
      <p>Lorem ipsum dolor sit amet.</p>
    </div>
    <button>Button</button>
  </div>
  <div class="children_element">
    <div>
      <p>Lorem ipsum dolor sit amet.</p>
    </div>
    <button>Button</button>
  </div>
</div>
isr3a4wc

isr3a4wc5#

由于OP已经使用了data-* global attribute(如data-children),OP可能会考虑显示/更新子计数更改的通用实现,这将完全不受DOM结构的影响,因此它总是会工作,而不管选择的标记名,类名和标记结构的嵌套。
例如,一个人可以从HTML for attribute中获得灵感。只是最小的一组相关的data-*属性,如...

<tagName
  data-child-count="2"
  data-child-count-for="23fd215b-4c2a-4861-b53a-5c517e67a56e"
>
  The amount of children of the next element is 2.
</tagName>

...还有...

<tagName data-child-count-id="23fd215b-4c2a-4861-b53a-5c517e67a56e">
  <!--
    ... any child structure like ...
  -->
  <tagName></tagName>
  <tagName></tagName>
</tagName>

...已经启用了显示/更新子计数更改的完全通用的实现。
例如,[data-child-count-for="23fd215b-4c2a-4861-b53a-5c517e67a56e"]标记了一个必须显示/更新子计数更改的元素。元素的相关dataset.childCountFor-值标识了预期发生子计数更改的元素。这正是由例如标记的元素。[data-child-count-id="23fd215b-4c2a-4861-b53a-5c517e67a56e"]
下面提供的示例代码显示了OP正在寻找的函数如何在这样一个通用标记上工作。

function updateChildCount(childCountNode) {
  const parentId = childCountNode.dataset.childCountFor;
  const parentNode =  document
    .querySelector(`[data-child-count-id="${ parentId ?? '' }"]`);

  if (parentNode) {
    const childCount = parentNode.childElementCount;

    childCountNode.dataset.childCount = childCount;
    childCountNode.textContent =
      `The amount of children of the next element is ${ childCount }.`;
  }
}
function updateAnyChildCount() {
  document
    .querySelectorAll('[data-child-count-for]')
    .forEach(updateChildCount);
}

// (re)render initial child-count values.
updateAnyChildCount();
body { margin: 0; }
p { margin: 4px 0; }
h2 { font-size: 1em; margin: 8px 0 4px 0; }
button[data-add],
button[data-delete] { float: right; margin-left: 2px; }
<h2
  data-child-count="unknown"
  data-child-count-for="23fd215b-4c2a-4861-b53a-5c517e67a56e"
>
  The amount of children of the next element is unknown.
</h2>

<div data-child-count-id="23fd215b-4c2a-4861-b53a-5c517e67a56e">
  <div>
    <p>Lorem ipsum dolor sit amet.</p>
    <button>Button</button>

    <button data-add>clone 'n append</button>
    <button data-delete>delete</button>
  </div>
</div>

<h2
  data-child-count="unknown"
  data-child-count-for="69e7b5bf-698f-4492-94a0-e48243fcf1f9"
>
  The amount of children of the next element is unknown.
</h2>

<div data-child-count-id="69e7b5bf-698f-4492-94a0-e48243fcf1f9">
  <div>
    <p>Lorem ipsum dolor sit amet.</p>
    <button>Button</button>

    <button data-add>clone 'n append</button>
    <button data-delete>delete</button>
  </div>
  <div>
    <p>Lorem ipsum dolor sit amet.</p>
    <button>Button</button>

    <button data-add>clone 'n append</button>
    <button data-delete>delete</button>
  </div>
  <div>
    <p>Lorem ipsum dolor sit amet.</p>
    <button>Button</button>

    <button data-add>clone 'n append</button>
    <button data-delete>delete</button>
  </div>
</div>

<script>
// - example's helper functionality which is going to invoke
//   the child-count update-function.
document
  .addEventListener('click', ({ target }) => {

    const button = target.closest('[data-add], [data-delete]');
    if (button) {

      // mutate the DOM.

      if (button.matches('[data-add]')) {
        const { parentNode } = button;

        parentNode
          .parentNode
          .appendChild(
            parentNode.cloneNode(true)
          );
      } else {
        button
          .parentNode
          .remove();
      }

      // invoke the child-count update-function.
      updateAnyChildCount();
    }
  });
</script>

上面的实现甚至可以更改为仅更新data-child-count属性。由于具有所述属性的元素总是通过该属性的值携带当前子计数,因此可以引入一个额外的单一CSS规则,简单如...

[data-child-count]:after {
  content: attr(data-child-count);
}

...以便始终确保最新的UI。
下面的示例代码稍微做了一些修改,只是证明了最后一条语句。

function updateChildCount(childCountNode) {
  const parentId = childCountNode.dataset.childCountFor;
  const parentNode =  document
    .querySelector(`[data-child-count-id="${ parentId ?? '' }"]`);

  if (parentNode) {
    childCountNode.dataset.childCount = parentNode.childElementCount;
  }
}
function updateAnyChildCount() {
  document
    .querySelectorAll('[data-child-count-for]')
    .forEach(updateChildCount);
}
body { margin: 0; }
p { margin: 4px 0; }
h2 { font-size: 1em; margin: 8px 0 4px 0; }
button[data-add],
button[data-delete] { float: right; margin-left: 2px; }

[data-child-count]:after {
  content: attr(data-child-count);
}
<h2
  data-child-count="1"
  data-child-count-for="23fd215b-4c2a-4861-b53a-5c517e67a56e"
>
  The amount of children of the next element is
</h2>

<div data-child-count-id="23fd215b-4c2a-4861-b53a-5c517e67a56e">
  <div>
    <p>Lorem ipsum dolor sit amet.</p>
    <button>Button</button>

    <button data-add>clone 'n append</button>
    <button data-delete>delete</button>
  </div>
</div>

<h2
  data-child-count="3"
  data-child-count-for="69e7b5bf-698f-4492-94a0-e48243fcf1f9"
>
  The next element's child-count is
</h2>

<div data-child-count-id="69e7b5bf-698f-4492-94a0-e48243fcf1f9">
  <div>
    <p>Lorem ipsum dolor sit amet.</p>
    <button>Button</button>

    <button data-add>clone 'n append</button>
    <button data-delete>delete</button>
  </div>
  <div>
    <p>Lorem ipsum dolor sit amet.</p>
    <button>Button</button>

    <button data-add>clone 'n append</button>
    <button data-delete>delete</button>
  </div>
  <div>
    <p>Lorem ipsum dolor sit amet.</p>
    <button>Button</button>

    <button data-add>clone 'n append</button>
    <button data-delete>delete</button>
  </div>
</div>

<script>
// - example's helper functionality which is going to invoke
//   the child-count update-function.
document
  .addEventListener('click', ({ target }) => {

    const button = target.closest('[data-add], [data-delete]');
    if (button) {

      // mutate the DOM.

      if (button.matches('[data-add]')) {
        const { parentNode } = button;

        parentNode
          .parentNode
          .appendChild(
            parentNode.cloneNode(true)
          );
      } else {
        button
          .parentNode
          .remove();
      }

      // invoke the child-count update-function.
      updateAnyChildCount();
    }
  });
</script>

此外,OP应该使用MutationObserverMutationRecord,后者作为记录数组提供给观察者的回调/处理函数。
至于OP的用例,我们只需要查看每个记录的NodeList,即addedNodesremovedNodes。每当节点列表中的任何一个至少携带单个元素引用时,就会发生子计数更改。
MutationObserver的决定实现了最大的通用实现,因为它将更改处理/管理完全与任何会触发DOM突变(例如元素插入和删除)的代码解耦。
为了不被迫观察整个DOM树,可以使用基于已经引入/提出的data-*属性的类似组件的方法,并与元素的相关dataset -property合作。
接下来提供的示例代码演示了如何识别这种松散耦合(标记和DOM方式)的组件,以及如何为每个这样的组件创建变化观察器。

function getChildCountCopy(template, count) {
  return template
    .replace(/\$\{\s*childCount\s*\}/g, count);
}
function renderChildCount(childCountNode, observedNode) {
  const childCount = observedNode.childElementCount;

  childCountNode.textContent = getChildCountCopy(
    childCountNode.dataset.childCountTemplate, childCount,
  );
  childCountNode.dataset.childCount = childCount;
}

function handleChildCountChangeForBoundNodes(
  records/*, observer,*/
) {
  const { childCountNode, observedNode } = this;

  records
    .forEach(record => {
      const { addedNodes, removedNodes } = record;

      if (addedNodes.length + removedNodes.length > 0) {

        renderChildCount(childCountNode, observedNode);
      }
    });
}
function initializeChildCountMutationObserver(
  childCountNode, observedNode,
) {
  const observer = new MutationObserver(
    handleChildCountChangeForBoundNodes
      .bind({
        childCountNode,
        observedNode,
      }),
  );
  observer
    .observe(observedNode, {
      childList: true,
      //  subtree: true,
      // attributes: true,
    });
}

function enableInstantChildCount(childCountNode) {
  const { childCountFor: observedId } = childCountNode.dataset;
  
  const observedNode = document
    .querySelector(`[data-child-count-id="${ observedId ?? '' }"]`);

  if (observedNode) {

    // (re)render initial/current child-count.
    renderChildCount(childCountNode, observedNode);

    // register handling of element related DOM mutations.
    initializeChildCountMutationObserver(
      childCountNode, observedNode,
    );
  }
}
document
  .querySelectorAll('[data-child-count-for]')
  .forEach(enableInstantChildCount);
body { margin: 0; }
p { margin: 4px 0; }
h2 { font-size: 1em; margin: 8px 0 4px 0; }
button[data-add],
button[data-delete] { float: right; margin-left: 2px; }
<h2
  data-child-count="1"
  data-child-count-for="23fd215b-4c2a-4861-b53a-5c517e67a56e"
  data-child-count-template="The amount of children of the next element is ${ childCount }."
>
  The amount of children of the next element is 1.
</h2>

<div data-child-count-id="23fd215b-4c2a-4861-b53a-5c517e67a56e">
  <div>
    <p>Lorem ipsum dolor sit amet.</p>
    <button>Button</button>

    <button data-add>clone 'n append</button>
    <button data-delete>delete</button>
  </div>
</div>

<h2
  data-child-count-for="69e7b5bf-698f-4492-94a0-e48243fcf1f9"
  data-child-count-template="The next element's child-count is ${ childCount }."
>
  The next element's child-count is unknown.
</h2>

<div data-child-count-id="69e7b5bf-698f-4492-94a0-e48243fcf1f9">
  <div>
    <p>Lorem ipsum dolor sit amet.</p>
    <button>Button</button>

    <button data-add>clone 'n append</button>
    <button data-delete>delete</button>
  </div>
  <div>
    <p>Lorem ipsum dolor sit amet.</p>
    <button>Button</button>

    <button data-add>clone 'n append</button>
    <button data-delete>delete</button>
  </div>
  <div>
    <p>Lorem ipsum dolor sit amet.</p>
    <button>Button</button>

    <button data-add>clone 'n append</button>
    <button data-delete>delete</button>
  </div>
</div>

<script>
// example's helper functionality which is going to trigger DOM mutations.
document
  .addEventListener('click', ({ target }) => {

    const button = target.closest('[data-add], [data-delete]');
    if (button) {

      // mutate the DOM.

      if (button.matches('[data-add]')) {
        const { parentNode } = button;

        parentNode
          .parentNode
          .appendChild(
            parentNode.cloneNode(true)
          );
      } else {
        button
          .parentNode
          .remove();
      }
    }
  });
</script>

相关问题