querySelector пошуку безпосередньо дітей


96

У мене є така функція, що нагадує jquery:

function(elem) {
    return $('> someselector', elem);
};

Питання в тому, як я можу зробити те саме querySelector()?

Проблема полягає >в тому, що селектор в querySelector()вимагає явного вказівки батьків. Чи є якийсь обхідний шлях?


Я додав відповідь, яка може працювати для вас краще, дайте мені знати;)
— csuwldcat

Відповіді:


119

Хоча це не повна відповідь, слід слідкувати за W3C Selector API v.2, який уже доступний у більшості браузерів, як на настільних, так і мобільних, крім IE (Edge, здається, підтримує): див. Повний список підтримки .

function(elem) {
  return elem.querySelectorAll(':scope > someselector');
};

12
Це правильна відповідь. Однак підтримка веб-переглядача обмежена, і для того, щоб скористатися нею, вам знадобиться обробка Для цього я створив scopedQuerySelectorShim .
— лазд


6
Насправді, :scopeвсе ще зазначено на drafts.csswg.org/selectors-4/#the-scope-pseudo . Поки що це лише <style scoped>видалений атрибут; незрозуміло, чи це також призведе до :scopeвидалення (оскільки це <style scoped>було важливим випадком використання :scope).
— Джон Меллор,

1
Який сюрприз: тільки Edge цього не підтримує
— Xenos

31

Ви не можете. Немає селектора, який би імітував вашу вихідну точку.

Те, як jQuery робить це (більше через спосіб, який qsaведе себе не до вподоби), полягає в тому, що вони перевіряють, чи elemє ID, і якщо ні, тимчасово додають ідентифікатор, а потім створюють повний рядок вибору.

В основному ви робите:

var sel = '> someselector';
var hadId = true;
if( !elem.id ) {
    hadID = false;
    elem.id = 'some_unique_value';
}

sel = '#' + elem.id + sel;

var result = document.querySelectorAll( sel );

if( !hadId ) {
    elem.id = '';
}

Це звичайно не код jQuery, але, як я пам’ятаю, це в основному те, що вони роблять. Не тільки в цій ситуації, але і в будь-якій ситуації, коли ви запускаєте селектор з контексту вкладеного елемента.

Вихідний код для Sizzle


1
Правильно на момент написання статті, але див. Відповідь @ avetisk для оновленого методу.
— лазд

23

Комплектація: поліфункція сфери

Як avetisk вже згадувалося селектор API 2 використовує :scopeпсевдо-селектор.
Щоб це працювало у всіх браузерах (які підтримують querySelector), ось поліфіл

(function(doc, proto) {
  try { // check if browser supports :scope natively
    doc.querySelector(':scope body');
  } catch (err) { // polyfill native methods if it doesn't
    ['querySelector', 'querySelectorAll'].forEach(function(method) {
      var nativ = proto[method];
      proto[method] = function(selectors) {
        if (/(^|,)\s*:scope/.test(selectors)) { // only if selectors contains :scope
          var id = this.id; // remember current element id
          this.id = 'ID_' + Date.now(); // assign new unique id
          selectors = selectors.replace(/((^|,)\s*):scope/g, '$1#' + this.id); // replace :scope with #ID
          var result = doc[method](selectors);
          this.id = id; // restore previous id
          return result;
        } else {
          return nativ.call(this, selectors); // use native code for other selectors
        }
      }
    });
  }
})(window.document, Element.prototype);

Використання

node.querySelector(':scope > someselector');
node.querySelectorAll(':scope > someselector');

З історичних причин моє попереднє рішення

На основі всіх відповідей

// Caution! Prototype extending
Node.prototype.find = function(selector) {
    if (/(^\s*|,\s*)>/.test(selector)) {
        if (!this.id) {
            this.id = 'ID_' + new Date().getTime();
            var removeId = true;
        }
        selector = selector.replace(/(^\s*|,\s*)>/g, '$1#' + this.id + ' >');
        var result = document.querySelectorAll(selector);
        if (removeId) {
            this.id = null;
        }
        return result;
    } else {
        return this.querySelectorAll(selector);
    }
};

Використання

elem.find('> a');

Date.now()не підтримується старими браузерами і може бути замінений наnew Date().getTime()
— Крістоф

4

Якщо ви знаєте назву тегу елемента, який ви розглядаєте, тоді ви можете використовувати його в селекторі, щоб досягти бажаного.

Наприклад, якщо у вас є a, <select>що має <option>s і <optgroups>, і ви хочете лише <option>s, які є його безпосередніми дітьми, а не ті, що знаходяться всередині <optgoups>:

<select>
  <option>iPhone</option>
  <optgroup>
    <option>Nokia</option>
    <option>Blackberry</option>
  </optgroup>
</select>

Тож, маючи посилання на елемент вибору, ви можете - на диво - отримати своїх безпосередніх дітей таким чином:

selectElement.querySelectorAll('select > option')

Здається, він працює в Chrome, Safari та Firefox, але не перевірявся в IE. = /


8
Попередження: Ця відповідь насправді не обмежує сферу застосування. Якщо візерунок повторюється на глибшому рівні гнізда, він все одно буде обраний, навіть якщо це не прямі діти. <ul id="start"> <li>A</li> <li> <ul> <li>b</li> <li>c</li> </ul> </li> </ul> var result = document.getElementById('start').querySelectorAll('ul>li'); -> Будуть відібрані всі 4 елементи li!
— Risord

1
Не дай Бог, <select>у одного батька було два елементи.
— Томаш Зато - Відновити Моніку

4

Якщо ви хочете врешті-решт знайти прямих дітей (а не напр. > div > span), Ви можете спробувати Element.matches () :

const elems = Array.from(elem.children).filter(e => e.matches('.my-class'))

2

ЗАЯВКА

Особисто я брав би відповідь від Patrick dw, і поставив +1 його відповідь, моя відповідь полягає у пошуку альтернативного рішення. Я не думаю, що це заслуговує проти.

Ось моя спроба:

function q(elem){
    var nodes = elem.querySelectorAll('someSeletor');
    console.log(nodes);
    for(var i = 0; i < nodes.length; i++){
        if(nodes[i].parentNode === elem) return nodes[i];
    }
}

див. http://jsfiddle.net/Lgaw5/8/


1
Пара проблем з цим. @disfated хоче, щоб це працювало querySelector, це означає, що :eq()його не можна використовувати. Навіть якби це могло, ваш селектор повернув би елемент, який є :eq()на його появі на сторінці, а не :eq()до індексу його батьківського елемента (звідки ви отримуєте idx).
— user113716

+1 про: eq () & querySelector. І я повинен додати контекст $ (elem) .parent ().
— Liangliang Zheng

На жаль, це теж не буде працювати. Коли селектор працює з контексту батьків, він починається з самої лівої дитини та знаходить усі відповідні елементи незалежно від того, наскільки глибоко вкладені, продовження продовжується. Так що скажіть, що elemце в індексі 4, але є попередній брат, який має інший tagName, але всередині нього вкладений елемент зі збігом tagName, який вкладений елемент буде включений в результати перед тим, на який ви орієнтуєтесь, знову скидаючи індекс. Ось приклад
— user113716

... у будь-якому випадку, що ви в кінцевому рахунку робите, це більш складна версія коду у питанні, яка працює. $('> someselector', elem);; o)
— користувач113716

Так, але вам потрібно було жорстко кодувати один приріст. Це вимагало б попередніх знань про це. Якщо я додаю ще один подібний елемент, він знову порушується. ; o) jsfiddle.net/Lgaw5/3
— користувач113716

1

Це працювало для мене:

Node.prototype.search = function(selector)
{
    if (selector.indexOf('@this') != -1)
    {
        if (!this.id)
            this.id = "ID" + new Date().getTime(); 
        while (selector.indexOf('@this') != -1)
            selector = selector.replace('@this', '#' + this.id);
        return document.querySelectorAll(selector);
    } else 
        return this.querySelectorAll(selector);
};

вам доведеться здати @ цю ключову роботу перед тим>, коли ви хочете шукати найближчих дітей.


Здається так само, як щойно прийнята відповідь ... До речі, який сенс дивного синтаксису "@ це"? Чому б просто не перевірити ">" за допомогою regexp?
— зневірився

1

Далі наведено спрощений загальний метод для запуску будь-якого запиту селектора CSS лише для прямих дочірніх елементів - він також враховує комбіновані запити, наприклад "foo[bar], baz.boo":

var count = 0;
function queryChildren(element, selector) {
  var id = element.id,
      guid = element.id = id || 'query_children_' + count++,
      attr = '#' + guid + ' > ',
      selector = attr + (selector + '').replace(',', ',' + attr, 'g');
  var result = element.parentNode.querySelectorAll(selector);
  if (!id) element.removeAttribute('id');
  return result;
}


*** Example Use ***

queryChildren(someElement, '.foo, .bar[xyz="123"]');

1

Існує відносна запит , що є досить зручною заміною для селектора запитів . Він поліфікує дитячий селектор '> *'і :scope(у тому числі IE8), а також нормалізує :rootселектор. Крім того, вона забезпечує певний спеціальний відносний pseudos як :closest, :parent, :prev, :next, на всякому разі.


0

перевірте, чи є елемент id ідентифікатора, додайте випадковий ідентифікатор і виконайте пошук на його основі

function(elem) {
      if(!elem.id)
          elem.id = Math.random().toString(36).substr(2, 10);
      return elem.querySelectorAll(elem.id + ' > someselector');
    };

зробить те саме, що і

$("> someselector",$(elem))
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.