How to implement "prevUntil" in Vanilla JavaScript without libraries?
Asked Answered
A

7

12

I need to implement the functionality of jQuery's prevUntil() method in Vanilla JavaScript.

I've got several <div> elements on the same level:

<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>

I'm trying to use an onclick event to find the event.target's previousSiblings until a certain criteria is reached (for example, a class name match) then stop.

How do I achieve this?

Ancillary answered 8/9, 2011 at 20:35 Comment(5)
@glowcoder: Personally I find it interesting to know what jQuery is actually doing.Hainaut
@pimvdb, jQuery is open source, don't hesitate to consult its source code if you are interested in what is it actually doing and most importantly how is it doing it.Andryc
@Darin Dimitrov: I know, I mean that there is nothing wrong with asking what's happening behind the scenes.Hainaut
@pimvdb, no, there is nothing wrong of course.Andryc
I can't use jQuery in the environment I am running this.Ancillary
K
13

This answer was previously published here in response to a similar question .

There are a few ways to do it.

Either one of the following should do the trick.

// METHOD A (ARRAY.FILTER, STRING.INDEXOF)
var siblings = function(node, children) {
    siblingList = children.filter(function(val) {
        return [node].indexOf(val) != -1;
    });
    return siblingList;
}

// METHOD B (FOR LOOP, IF STATEMENT, ARRAY.PUSH)
var siblings = function(node, children) {
    var siblingList = [];
    for (var n = children.length - 1; n >= 0; n--) {
        if (children[n] != node) {
            siblingList.push(children[n]);
        }  
    }
    return siblingList;
}

// METHOD C (STRING.INDEXOF, ARRAY.SPLICE)
var siblings = function(node, children) {
   siblingList = children;
   index = siblingList.indexOf(node);
   if(index != -1) {
       siblingList.splice(index, 1);
   }
   return siblingList;
}

FYI: The jQuery code-base is a great resource for observing Grade A Javascript.

Here is an excellent tool that reveals the jQuery code-base in a very streamlined way. http://james.padolsey.com/jquery/

Kitkitchen answered 2/4, 2014 at 0:11 Comment(0)
C
8

Example Using previousElementSibling:

    var className = "needle";
    var element = clickedElement;
    while(element.previousElementSibling && element.previousElementSibling.className != className) {
       element = element.previousElementSibling;
    }
    element.previousElementSibling; // the element or null
Costa answered 8/9, 2011 at 20:45 Comment(3)
This element.previousSibling.className is problematical. If no previous sibling has the desired class, element.previousSibling will eventually return null, so element.previousSibling.className will throw an error.Bilection
That was in order to prevent an infinite loop! Just Kidding. I've updatedCosta
Thank you, guy-with-long-name.Ancillary
H
6

Use .children in combination with .parentNode. Then filter the NodeList, after converting it into an array: http://jsfiddle.net/pimvdb/DYSAm/.

var div = document.getElementsByTagName('div')[0];
var siblings = [].slice.call(div.parentNode.children) // convert to array
                 .filter(function(v) { return v !== div }); // remove element itself
console.log(siblings);
Hainaut answered 8/9, 2011 at 20:43 Comment(1)
NodeList to Array casting with [].slice.call( nl )? Nice :)Bilection
H
3

How about this:

while ( node = node.previousElementSibling ) {
    if ( ( ' ' + node.className + ' ' ).indexOf( 'foo' ) !== -1 ) {
        // found; do your thing
        break;
    }
}

Don't bother telling me that this doesn't work in IE8...

Hurty answered 8/9, 2011 at 20:46 Comment(3)
Would you like to be deceived? Since I won't deceive you I can not answer your question because you asked me not to.Hesperian
@Wayne No, I don't like to be deceived. I didn't ask you not to answer my question. Please answer it! And what did I ask? I thought I answered a question, not asked one.Bilection
Sorry misunderstanding on my part. I thought you ment don't tell me it does not work in IE8 literally ... not that you already know.Hesperian
A
2

There is a previousSibling property in the HTML DOM

Here is some reference

http://reference.sitepoint.com/javascript/Node/previousSibling

Alsup answered 8/9, 2011 at 20:40 Comment(2)
The property previousSibling is a reference to a DOM node, not a function.Bilection
I understand that completely. But with some simple iteration you can use this to find the previous element which matches his conditions.Alsup
R
2

Just take a look at how jQuery does it.

prevUntil: function( elem, i, until ) {
    return jQuery.dir( elem, "previousSibling", until );
},

Which uses a while / looping function caled dir(). The prevUntil just keeps going until previousSibling is the same as the until element.

dir: function( elem, dir, until ) {
    var matched = [],
        cur = elem[ dir ];

    while ( cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !jQuery( cur ).is( until )) ) {
        if ( cur.nodeType === 1 ) {
            matched.push( cur );
        }
        cur = cur[dir];
    }
    return matched;
},
Rigmarole answered 8/9, 2011 at 20:42 Comment(0)
E
1

You could use indexOf to determine if the index of the siblings are less than the index of the target element:

// jshint esversion: 9

// get the target node
const node = document.querySelector("div:nth-child(3)");

// get the target node's index
const node_index = node.parentNode.indexOf(node);

// get the siblings of the target node
const siblings = node => [...node.parentNode.children].filter(child => 
  child !== node
);
console.log(siblings);

// get the prevUntil
const class_name = "\bmy_class\b";
const prevUntil = siblings.filter((sibling, i) =>
  i < node_index && (sibling.getAttribute("class") || "").includes(class_name)
);
console.log(prevUntil);

Good luck.

Ewold answered 21/10, 2019 at 14:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.