admin管理员组

文章数量:1391850

I've got a element tree that looks like.

<div>
  <div required>
    <div content></div>
  </div>
  <div>
    <div content></div>
  </div>
</div>

How can I do a querySelector on the root div that gets all elements tagged as "content" without getting any that are below a parent element with the "required" attribute? I'm thinking something like.

querySelectorAll('[content]:not([required]')

But that query would fetch all elements tagged as content and not tagged as required rather than all elements tagged as content that aren't below a required element.

I've got a element tree that looks like.

<div>
  <div required>
    <div content></div>
  </div>
  <div>
    <div content></div>
  </div>
</div>

How can I do a querySelector on the root div that gets all elements tagged as "content" without getting any that are below a parent element with the "required" attribute? I'm thinking something like.

querySelectorAll('[content]:not([required]')

But that query would fetch all elements tagged as content and not tagged as required rather than all elements tagged as content that aren't below a required element.

Share Improve this question asked Mar 5, 2020 at 3:04 user3875080user3875080 751 silver badge5 bronze badges 5
  • document.querySelectorAll(‘div:not([required]) [content]’) should get all elements which have the content attribute which aren’t descendants of an element with the required attribute. – Benjamin James Kippax Commented Mar 5, 2020 at 3:09
  • 1 @BenjaminJamesKippax That won't work, because as long as any ancestor lacks the required attribute, that ancestor will match, and the child [content] will be included. – CertainPerformance Commented Mar 5, 2020 at 3:18
  • @CertainPerformance use the direct descendant selector then? :not([required]) > [content] – Benjamin James Kippax Commented Mar 5, 2020 at 3:20
  • @OP are your content elements only ever direct children of a required element, or can the required be some ancestor? – CertainPerformance Commented Mar 5, 2020 at 3:23
  • @user3875080 Will you check to see if my solution works? – Miaplacidus Commented Mar 5, 2020 at 3:26
Add a ment  | 

2 Answers 2

Reset to default 3

Unfortunately, :not only accepts a simple selector, so :not([required] [content]) to match a [content] which is not a child of [content] won't work. You'll have to programmatically filter the elements after selecting them:

const notRequiredContents = [...document.querySelectorAll('[content]')]
  .filter(elm => !elm.closest('[required]'));
console.log(notRequiredContents);
<div>
  <div required>
    <div content></div>
  </div>
  <div>
    <div content></div>
  </div>
</div>

It'd be theoretically possible to do this with only a query string by chaining :not([required]) with the descendant selector, but it looks really ugly and repetitive and shouldn't be done:

const notRequiredContents = document.querySelectorAll(`
  body > :not([required]) > [content],
  body > :not([required]) > :not([required]) > [content],
  body > :not([required]) > :not([required]) > :not([required]) > [content],
  body > :not([required]) > :not([required]) > :not([required]) > :not([required]) > [content]
`);
// continue above pattern for as much nesting as may exist
console.log(notRequiredContents[0], notRequiredContents.length);
<div>
  <div required>
    <div content>a</div>
  </div>
  <div>
    <div content>b</div>
  </div>
</div>

Repl Example

JS

const query = document.querySelectorAll('div :not([required]) [content]')
query.forEach((element) => {
  console.log(element.innerHTML)
})

HTML

<div>
  <div required>
    <div content>NO</div>
  </div>
  <div>
    <div content>YES</div>
    <div>
      <div content>YES</div>
    </div>
  </div>
</div>

本文标签: javascriptquerySelectorAll() exclude attribute and children of that attributeStack Overflow