Vanilla JavaScript equivalent of jQuery(…).parent().nextAll(selector)

I’m trying to have a vanilla JavaScript selector (without using jQuery) to get the same selection as shown below:

$('[data-automationid=pictureupload_1]').parent().nextAll('input[type=text]')

Can someone please help me? I’ve been struggling with it.

13 thoughts on “Vanilla JavaScript equivalent of jQuery(…).parent().nextAll(selector)”

    • Use document.querySelector('[data-automationid=pictureupload_1]') to select the starting-node.
    • Get with parentElement.children all the siblings from the parent-node (including the parent itself).
    • Iterate through the siblings until the parent-node is founded.
    • Look for all other siblings if they are INPUT-nodes and from type text (=1).
    • Collect them in an array.

    For demonstration iterate over the result and change the background via test-class.

    If you want try: https://jsfiddle.net/7p8wt4km/2/

    let result = [];
    let par = document.querySelector('[data-automationid=pictureupload_1]').parentElement;
    let sibling = par.parentElement.children;
    let found = false;
    for (let i=0; i< sibling.length; i++) {
        if (!found && sibling[i] ===par) {
            found = true;
            continue;
        } else if (found) {
            let sib = sibling[i];
            if (sib.nodeName !== 'INPUT' || sib.nodeType!= 1) continue;
            result.push(sib);
        }
    }
    
    result.forEach(el => { el.classList.add('test');});
    .test { background: green; }
    <div>
      <div>
        Sibling 0
      </div>
      <div>
        Parent
        <div data-automationid='pictureupload_1'>
          pictureupload_1
        </div>
      </div>
      <input type='text'>
      <div type='text'>
        Sibling 2
      </div>
      <input type='test'>
      <input type='checkbox'>
    </div>
    Reply
  1. There is no nextAll method in DOM as far as I know, so it is a bit tricky to do that without using jQuery.

    We can use a generator to iterate and filter nextElementSibling like this:

    function* myNextAll(e, selector) {
       while (e = e.nextElementSibling) {
           if ( e.matches(selector) ) {
              yield e;
           }
       }
    }
    
    let e1 = document.querySelector("[data-automationid=pictureupload_1]").parentElement;
    
    let siblings = [...myNextAll(e1, "input[type=text]")];
    
    
    Reply
  2. The normal jQuery selector function and the parent method should be clear:

    document.querySelector("[data-automationid=pictureupload_1]").parentNode
    

    nextAll doesn’t currently have a vanilla JavaScript equivalent, but we can make one out of native DOM methods.
    There are two ways of approaching it:

    1. Select all children first, then filter all matching siblings after a certain element, or
    2. using loops.

    1. Select all children first, then filter all matching siblings after a certain element

    Getting the set of siblings can be partially achieved with .parentNode.children.
    But don’t worry about selecting the parent element itself, because filtering by DOM order is easy.
    This uses Node.prototype.compareDocumentPosition and Element.prototype.matches.
    I’ve also included some optional chaining so that if parentReference === document, parentReference.parentNode.children won’t throw an error.
    It defaults to an empty array, then.

    const selectParentMatchingNextSiblings = (jQuerySelector, siblingSelector) => {
        const parentReference = document.querySelector(jQuerySelector).parentNode;
        
        return Array.from(parentReference.parentNode?.children ?? [])
          .filter((element) => parentReference.compareDocumentPosition(element) & Document.DOCUMENT_POSITION_FOLLOWING && element.matches(siblingSelector));
      };
    
    selectParentMatchingNextSiblings("[data-automationid=pictureupload_1]", "input[type=text]");
    

    Alternative way with Ranges

    The Range API can also be used for document order comparison.

    const selectParentMatchingNextSiblings = (jQuerySelector, siblingSelector) => {
        const parentReference = document.querySelector(jQuerySelector).parentNode,
          nextSiblings = document.createRange();
        
        nextSiblings.setStartAfter(parentReference);
        nextSiblings.setEndAfter(parentReference.parentNode?.lastElementChild ?? parentReference);
        
        return Array.from(parentReference.parentNode?.children ?? [])
          .filter((element) => nextSiblings.comparePoint(element, 0) === 0 && element.matches(siblingSelector));
      };
    
    selectParentMatchingNextSiblings("[data-automationid=pictureupload_1]", "input[type=text]");
    

    Note that this one won’t work with "html" as the first selector.

    2. Using loops

    const selectMatchingNextSiblings = (jQuerySelector, siblingSelector) => {
        const parentReference = document.querySelector(jQuerySelector).parentNode,
          result = [];
        let currentElement = parentReference.nextElementSibling;
        
        while(currentElement){
          if(currentElement.matches(siblingSelector)){
            result.push(currentElement);
          }
          
          currentElement = currentElement.nextElementSibling;
        }
        
        return result;
      };
    
    selectParentMatchingNextSiblings("[data-automationid=pictureupload_1]", "input[type=text]");
    

    This should be self-explanatory.
    The options from the other answers are also fine.

    Reply
  3. You can try my code get with index element

    const getIndex = (node, groupNode) => {
        return [...groupNode].indexOf(node);
    }
    
    const nextAll = (elm) => {
        var allChildren = elm.parentNode.children;
        var index = getIndex(elm, allChildren);
        allChildren = [...allChildren].filter((item) => {
            return getIndex(item, allChildren) > index;
        });
        return allChildren;
    }
    
    Reply

Leave a Comment