JS querySelectorAll для элементов определенного деда, если только их родитель не имеет определенного идентификатора - PullRequest
0 голосов
/ 05 ноября 2019

Я пытаюсь выбрать input[type="submit"] элементы, которые являются потомками (не прямыми) из article элементов с атрибутом id, начинающимся с post- (то есть article[id^="post-"]), но не если они также являются потомкамиэлемента #custom-footer.

Возвращает оба элемента input из приведенного ниже кода.

document.querySelectorAll('article[id^="post-"] :not(#custom-footer) input[type="submit"]')

То же самое с этим.

document.querySelectorAll(':not(#custom-footer) input[type="submit"]')

HTML:

<article id="post-2">

    <div> <!-- multiple nested divs -->
        <div>
            <input type="submit"> <!-- match me -->
        </div>
    </div>

    <div> <!-- multiple nested divs -->
        <div>
            <div id="custom-footer">
                <input type="submit"> <!-- DO NOT match me -->
            </div>
        </div>
    </div>

</article>

Ответы [ 2 ]

0 голосов
/ 05 ноября 2019

ОК, если вы хотите получить дочерние входы статей, но исключить какие-либо из элементов #custom-footer, вам нужно будет реализовать какую-то "ближайшую" функцию. Вы просто идете по родительской линии и проверяете селекторы и игнорируете те, которые соответствуют тому, который вы не хотите включать.

/* Polyfill */
if (!Element.prototype.matches) {
  Element.prototype.matches =
    Element.prototype.matchesSelector ||
    Element.prototype.mozMatchesSelector ||
    Element.prototype.msMatchesSelector ||
    Element.prototype.oMatchesSelector ||
    Element.prototype.webkitMatchesSelector ||
    function(s) {
      var matches = (this.document || this.ownerDocument).querySelectorAll(s),
        i = matches.length;
      while (--i >= 0 && matches.item(i) !== this) {}
      return i > -1;
    };
}

let childSel = 'article[id^="post-"] input[type="submit"]';
let parentSel = 'article[id^="post-"]'; /* Optional */
let excludeParentSel = '#custom-footer';
let filteredButtons = Array.from(document.querySelectorAll(childSel)).filter((b) => {
  return getClosest(b, parentSel) != null && getClosest(b, excludeParentSel) == null;
});

filteredButtons.forEach(btn => console.log(btn.className)); // Only 2

// See: https://gomakethings.com/how-to-get-the-closest-parent-element-with-a-matching-selector-using-vanilla-javascript/#browser-compatibility
function getClosest(elem, selector) {
  for (; elem && elem !== document; elem = elem.parentNode) {
    if (elem.matches(selector)) return elem;
  }
  return null;
};
<article id="post-2">
  <input type="submit" class="btn-valid-1">
  <!-- match me -->
  <br />
  <div>
    <div>
      <div>
        <div>
          <div>
            <input type="submit" class="btn-valid-2">
          </div>
        </div>
      </div>
    </div>
  </div>
  <!-- match me -->
  <div id="custom-footer">
    <input type="submit" class="btn-invalid">
    <!-- DO NOT match me -->
  </div>
</article>
0 голосов
/ 05 ноября 2019

Когда у вас есть селектор вида a :not(b) c, он выбирает элементы, соответствующие c, которые являются потомками элементов, не соответствующих b, где последние являются потомками элементов, соответствующих a.

Это, как правило, бесполезно, поскольку между a и c может быть несколько узлов, и только один из них должен соответствовать :not(b) для выбранного элемента. Для этого требуется, чтобы по крайней мере один предок c ниже a не совпадал с b - т.е. он выбирает c, если не все предки выше a соответствуют b - вероятно, не то, чтовы намеревались.

Если вам нужны только элементы, которые не дочерние элементов, соответствующих b, напишите a :not(b) > c, чтобы использовать дочерний селектор вместо потомка селектора. Для этого родительский из c не должен совпадать b.

В качестве альтернативы, если :not(b) действительно предназначен для применения к предкам, а не только к родителю, то это невообще возможно. Требуемый селектор будет выглядеть как a c:not(b *), соответствует c с без предков ниже a соответствует b. Однако спецификация CSS запрещает использование потомков в :not(), поэтому это не сработает. Это означает, что вы не можете использовать querySelectorAll для выбора элементов с без предков , соответствующих b. В качестве обходного пути вы могли бы написать вспомогательную функцию, чтобы проверить, имеет ли элемент соответствие предка b, а затем использовать Array.filter для исключения элементов, которые выполняют.

...