-2

I want to test if an element has a specific class on it. This if statement works:

if (children[i].className === siblingClass)

However, it only works if children[i] has only one class, and it is exactly the same as the value of siblingClass. I want to make the function a little more generalized, to test if children[i] has siblingClass even if it also has other classes as well.

So I tried the classList.contains function, like this:

if (children[i].classList.contains(siblingClass))

But, when I try it (in Chrome), the console log says:

Uncaught TypeError: Cannot read property 'contains' of undefined

How do I create a test to see if children[i] has siblingClass listed among one or more classes it may have?

Please note I would prefer to have a pure Javascript solution that does not require jQuery if possible.

This is the full function where the if statement appears:

function getSiblingByClass(currentObject, siblingClass)
{
    var parentofSelected = currentObject.parentNode,
        children = parentofSelected.childNodes,
        element,
        i;

    for (i = 0; i < children.length; i++)
    {
        if (children[i].classList.contains(siblingClass))
        {
            element = children[i];
            break;
        }
    }
    return element;
}

This function is called from within other functions when I want to search among element siblings for an element that has a specified class. It gets the parent of the current element, loops through all children, and returns the first match. Or, at least that's the idea.

Here is some HTML which calls a function which in turn calls the above function:

   <div>
        <span class="fee">hello world</span>
        <span class="fi">Blah blah blah</span>
        <button class="foo" onclick="copySiblingDataByClass(this, 'fi');">Click here</button>
    </div>

copySiblingDataByClass() would in turn call getSiblingByClass(), and pass itself and the name of the class to be selected.

Questioner
  • 7,133
  • 16
  • 61
  • 94
  • 2
    What is `children[i]`? What browser version are you using? Please create an [mcve] – T J Jul 31 '17 at 09:01
  • 2
    @Satpal The error clearly indicates that `children[i]` IS defined, `children[i].classList` is what's `undefined`. If `children[i]` was `undefined` the error would say **Cannot read property 'classList' of undefined**. This issue is most likely because `children[i]` is not an element node – Lennholm Jul 31 '17 at 09:07
  • 2
    @ the commenters :: Kindly consider that OP is not an idiot and that you may not know the exact particulars of the context -- before jumping to conclusions and down-vote as result of these assumptions. –  Jul 31 '17 at 09:37
  • @TJ, As mentioned, the browser I'm testing with is Chrome. I've added the full function as you requested. – Questioner Aug 01 '17 at 02:06
  • Latest version of chrome? You could be using a version that does not support `classList`, that is why version is important – T J Aug 01 '17 at 08:09
  • @TJ, Version 60.0.3112.78 (Official Build) (64-bit). – Questioner Aug 01 '17 at 08:26

4 Answers4

1

Below are some ways you can try:

var div = document.getElementById('div');

// classList method
if (div.classList.contains('world'))
    console.log(true);
else
    console.log(false);

// RegExp method
if (/\s|^\wworld\s/.test(div.className))
    console.log(true);
else
    console.log(false);

// Converting classname into array to use array method
if (div.className.split(' ').includes('world'))
    console.log(true);
else
    console.log(false);
    
// Also using array method, but more compatible
if (div.className.split(' ').indexOf('world') > -1)
    console.log(true);
else
    console.log(false);

// Borrowing array method to use on classList
if (Array.prototype.includes.call(div.classList, 'world'))
    console.log(true);
else
    console.log(false);
<div id="div" class="hello world foo bar"></div>

There is a lot more ways to do as you become more creative.

PS

From your example, it seems your children[i] is probably not an element. A non-element node doesn't have classList, hence the undefined.

One other possibility is that your Chrome browser is extremely outdated and obsolete.

yqlim
  • 6,898
  • 3
  • 19
  • 43
1

It appears that your code is part of a for loop, so, consider the following:

var children=[].slice.call(document.getElementById('someID').childNodes);
var siblingClass = 'someClassName'; // for example

for(var i in children)
{
    if(!children.hasOwnProperty(i)){continue}; // get rid of "undefined"
    if(children[i].className.indexOf(siblingClass) > -1)
    {
        // you other code here 
    }
}
  • Thank you for responding. I'm having a little difficulty parsing how to apply `slice.call` and `hasOwnProperty` to my use case. I've added the full function to my question in hopes that it may add clarity to my issue. – Questioner Aug 01 '17 at 02:08
  • @Questioner - the `[].slice.call()` part is just a way to ensure you're dealing with an "array-like" result; so, it just converts/ensures the childNodes as enumerable list. By using this you can now also use something like `childNodes.forEach(function(node){/* your code here*/});` -- then you won't need `hasOwnProperty` anyway. –  Aug 01 '17 at 08:41
1

After much testing, and following suggestions here, I simply could not deduce why classList.contains() would not work for me. The MDN says it's been supported in Chrome since version 8 and I am running version 60.

However, I need to progress my code into production, so I can't devote any more time to the mystery of it. Fortunately, I found a function in this answer that will successfully select one class out of many classes attached to an object.

function hasClass(element, cls)
{
    return (' ' + element.className + ' ').indexOf(' ' + cls + ' ') > -1;
}

With this function, my if statement now looks like this:

if (hasClass(children[i], siblingClass))

... and it works perfectly, so this is the solution I will be using.

If someone can show me how to get classList.contains() to work, or explain why it doesn't, I'd happily mark that answer as correct.

Questioner
  • 7,133
  • 16
  • 61
  • 94
1

Using Node.contains on a string or array is not what it was meant to do, however; taking your function you can make it work that way.

Consider the following "prototype upgrade" - place at the beginning of your JS code, it provides the functionality globally:

Object.defineProperty(String.prototype,'contains',{enumerable:false, configurable:false, writable:false, value:function(find)
{
    return ((' '+this+' ').indexOf(' '+find+' ') > -1);
}});


Now you can simply use it anywhere in your code like this:

someNode.className.contains('foo'); // returns a boolean


..however, if it were me i'd simply use your function applied to all elements automatically, like so:

Object.defineProperty(Element.prototype,'hasClass',{enumerable:false, configurable:false, writable:false, value:function(name)
{
    return ((' '+this.className+' ').indexOf(' '+name+' ') > -1);
}});


Now simply use it like:

someNode.hasClass('foo');