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.