querySelector search immediate children

JavascriptCss SelectorsSelectors Api

Javascript Problem Overview


I have some jquery-like function:

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

The question is how can i do the same with querySelector()?

The problem is > selector in querySelector() requires parent to be explicitly specified. Is there any workaround?

Javascript Solutions


Solution 1 - Javascript

Though it's not a full answer, you should keep an eye on the W3C Selector API v.2 which is already available in most browser, both desktop and mobile, except IE (Edge seems to support): see full support list.

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

Solution 2 - Javascript

You can't. There's no selector that will simulate your starting point.

The way jQuery does it (more because of a way that qsa behaves that is not to their liking), is that they check to see if elem has an ID, and if not, they temporarily add an ID, then create a full selector string.

Basically you'd do:

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 = '';
}

This certainly isn't jQuery code, but from what I remember, it is basically what they do. Not just in this situation, but in any situation where you're running a selector from the context of a nested element.

Source code for Sizzle

Solution 3 - Javascript

Complete :scope polyfill

As avetisk has mentioned Selectors API 2 uses :scope pseudo-selector.
To make this work in all browsers (that support querySelector) here is the polyfill

(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);

Usage

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

For historical reasons, my previous solution

Based on all answers

// 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);
    }
};

Usage

elem.find('> a');

Solution 4 - Javascript

If you want to eventually find direct children (and not e.g. > div > span), you can try Element.matches():

const elem = document.body
const elems = [...elem.children].filter(e => e.matches('b'))

console.log(elems)

<a>1</a>
<b>2</b>
<b>3</b>
<b>4</b>
<s>5</s>

Solution 5 - Javascript

CLAIM

Personally I would take the answer from patrick dw, and +1 his answer, my answer is for seeking alternative solution. I don't think it deserves a downvote.

Here is my attempt :

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];
    }
}

see http://jsfiddle.net/Lgaw5/8/

Solution 6 - Javascript

If you know the tag name of the element that you’re looking into, then you can use it in the selector to achieve what you want.

For example if you have a <select> that has <option>s and <optgroups>, and you only want the <option>s that are its immediate children, not the ones inside <optgoups>:

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

So, having a reference to the select element, you can — surprisingly — get its immediate children like this:

selectElement.querySelectorAll('select > option')

It seems to work in Chrome, Safari, and Firefox, but didn’t test in IEs. =/

Solution 7 - Javascript

The following is a simplified, generic method for running any CSS selector query over only direct children - it also accounts for combined queries, like "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"]');

Solution 8 - Javascript

There’s a query-relative lib, which is quite handy replacement for query-selector. It polyfills children selector '> *' and :scope (inc. IE8), as well as normalizes :root selector. Also it provides some special relative pseudos like :closest, :parent, :prev, :next, just in case.

Solution 9 - Javascript

That worked for me:

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);
};

you will have to pass the @this keywork before the > when you want to search for immediate children.

Solution 10 - Javascript

check if element have id else add random id and do search based on it

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

will do same thing as

$("> someselector",$(elem))

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestiondisfatedView Question on Stackoverflow
Solution 1 - JavascriptavetiskView Answer on Stackoverflow
Solution 2 - Javascriptuser113716View Answer on Stackoverflow
Solution 3 - JavascriptdisfatedView Answer on Stackoverflow
Solution 4 - JavascripttuomassaloView Answer on Stackoverflow
Solution 5 - JavascriptLiangliang ZhengView Answer on Stackoverflow
Solution 6 - JavascriptVlad GURDIGAView Answer on Stackoverflow
Solution 7 - JavascriptcsuwldcatView Answer on Stackoverflow
Solution 8 - Javascriptdy_View Answer on Stackoverflow
Solution 9 - JavascriptDaviView Answer on Stackoverflow
Solution 10 - JavascriptZeeshan AnjumView Answer on Stackoverflow