How to check className in a switch statement when an element has multiple classes
Asked Answered
M

7

8

In the example below I just want the option clicked to display in an alert. I'm trying to use a switch statement to determine what class was clicked. My example would work if my divs did not each contain more than one class. I tried using classList.contains in my switch statement to no avail. Is there a way I can get this working without changing my use of a switch statement?

function optionClicked(){
  switch( this.className ){
    case 'option1':
      alert( 'user clicked option1' );
      break;
    case 'option2':
      alert( 'user clicked option2' );
      break;
    case 'option3':
      alert( 'user clicked option3' );
      break;      
  }
}

function optionTabs(){
  var optionTabs = document.querySelectorAll( 'div' ),
  i = 0;
  
  for( i; i < optionTabs.length; i++ ){
    optionTabs[ i ].addEventListener( 'click', optionClicked );
  }
}

optionTabs();
html {
  background-color: #eee;
  font-family: sans-serif;
}
div {
  cursor: pointer;
  margin: 1.1rem;
  padding: 1rem;
  background-color: #fff;
  letter-spacing: 0.05rem;
  border-radius: 1rem;
}
div:hover {
  background-color: #555;
  color: #eee;
}
<div class="option1 more">option 1</div>
<div class="option2 classes">option 2</div>
<div class="option3 here">option 3</div>
Mathamathe answered 27/10, 2017 at 20:35 Comment(1)
Well you could try the switch(true) "trick", and put classList.contains() in as the case condition ...Brickle
A
15

The following should work in your switch statement:

function optionClicked(){
  var cls = this.classList;
  switch( true ){
    case cls.contains('option1'):
      alert( 'option1' );
      break;
    case cls.contains('option2'):
      alert( 'option2' );
      break;
    case cls.contains('option3'):
      alert( 'option3' );
      break;      
  }
}

function optionTabs(){
  var optionTabs = document.querySelectorAll( 'div' ),
  i = 0;
  
  for( i; i < optionTabs.length; i++ ){
    optionTabs[ i ].addEventListener( 'click', optionClicked );
  }
}

optionTabs();
html {
  background-color: #eee;
  font-family: sans-serif;
}
div {
  cursor: pointer;
  margin: 1rem;
  padding: 1rem;
  background-color: #fff;
  letter-spacing: 0.05rem;
}
div:hover {
  background-color: #555;
  color: #eee;
}
<div class="option1 more">option 1</div>
<div class="option2 classes">option 2</div>
<div class="option3 here">option 3</div>
Articulator answered 27/10, 2017 at 20:48 Comment(2)
I deconstructed this.classList to make it cleaner.Articulator
This works. For styling points, I'd point out that assigning the cls variable reduces repeating oneself in the code only a little bit as it still repeatedly calls .contains(). The switch statement itself is redundant and you could write less code by switching each case to a simple if block. I added a brief answer about how having an array of anonymous handler functions indexed by each class name could be more succinct.Pericranium
S
4

Use a RegExp to extract relevant classes from the className:

function optionClicked(){
  switch((this.className.match(/\boption\d+\b/) || [])[0]){
    case 'option1':
      alert( 'user clicked option1' );
      break;
    case 'option2':
      alert( 'user clicked option2' );
      break;
    case 'option3':
      alert( 'user clicked option3' );
      break;      
  }
}

function optionTabs(){
  var optionTabs = document.querySelectorAll( 'div' ),
  i = 0;
  
  for( i; i < optionTabs.length; i++ ){
    optionTabs[ i ].addEventListener( 'click', optionClicked );
  }
}

optionTabs();
html {
  background-color: #eee;
  font-family: sans-serif;
}
div {
  cursor: pointer;
  margin: 1rem;
  padding: 1rem;
  background-color: #fff;
  letter-spacing: 0.05rem;
}
div:hover {
  background-color: #555;
  color: #eee;
}
<div class="option1 more">option 1</div>
<div class="option2 classes">option 2</div>
<div class="option3 here">option 3</div>
Signalman answered 27/10, 2017 at 20:47 Comment(0)
M
4

You have to use this.classList.contains, that returns a boolean.

function optionClicked(){
  switch(true){
    case this.classList.contains('option1'):
      alert( 'user clicked option1' );
      break;
    case this.classList.contains('option2'):
      alert( 'user clicked option2' );
      break;
    case this.classList.contains('option3'):
      alert( 'user clicked option3' );
      break;
  }
}
Maddock answered 27/10, 2017 at 20:53 Comment(0)
L
1

I did something a bit different than a switch. Created an array full of your options and filtered out the result only if the element's classes match an option in the options array.

You would need to then do your check on the filtered result. Ori's answer is a more straight to the point.

function optionClicked(){
  var classes = this.className.split(' '),
      options = ['option1', 'option2', 'option3'];
      
  var clickedOption = classes.filter(function(c) {
    return options.indexOf(c) >= 0;
  });
  
  alert(clickedOption)
  
}

function optionTabs(){
  var optionTabs = document.querySelectorAll( 'div' ),
  i = 0;
  
  for( i; i < optionTabs.length; i++ ){
    optionTabs[ i ].addEventListener( 'click', optionClicked );
  }
}

optionTabs();
html {
  background-color: #eee;
  font-family: sans-serif;
}
div {
  cursor: pointer;
  margin: 1rem;
  padding: 1rem;
  background-color: #fff;
  letter-spacing: 0.05rem;
}
div:hover {
  background-color: #555;
  color: #eee;
}
<div class="more option1">option 1</div>
<div class="option2 classes">option 2</div>
<div class="option3 here">option 3</div>
Lennox answered 27/10, 2017 at 20:47 Comment(0)
R
1

Adding this to your optionClicked() function should work!

function optionClicked(){
  let classNames = this.className.split(" ")

   className = classNames.find(
    function (val){
      return /option\d/.test(val)
    }
   )
// switch
}

Full Function

function optionClicked(){
 let classNames = this.className.split(" ")

 className = classNames.find(
  function (val){
    return /option\d/.test(val)
  }
 )
switch( className ){
 case 'option1':
  alert( 'option1' );
  break;
 case 'option2':
   alert( 'option2' );
   break;
 case 'option3':
   alert( 'option3' );
   break;      
 }
}

function optionTabs(){
  var optionTabs = document.querySelectorAll( 'div' ),
  i = 0;

  for( i; i < optionTabs.length; i++ ){
    optionTabs[ i ].addEventListener( 'click', optionClicked );
  }
}

optionTabs();
Ram answered 27/10, 2017 at 21:7 Comment(0)
G
1

classList has a parameter value and instead of creating an array or using switch(true), you can simply check for the existence of a string:

switch (element.classList.value) {
       case 'new-elem' :
       console.log(1);
          break;
       case 'data-elem':
       console.log(2);
          break;
}
Grabble answered 28/3, 2022 at 18:36 Comment(0)
P
0

In my opinion, the switch statement is cluttering the code as it's likely you will always use break statements immediately following every case.

Instead to not repeat yourself, you can make an array of handlers per each class name (by splitting the classList.value string by whitespace) and check if there's an existing handler for each class name.

const handlers = {
  'option1': () => alert('User clicked option1'),
  'option2': () => alert('User clicked option2'),
  'option3': () => alert('User clicked option3'),
};
this.className.value.split(/\s+/).forEach(className => handlers[className]?.());

Note: This isn't precisely logically equivalent, but given the likelihood that the presence of one option excludes the presence of all the other options, then this is a cleaner and more succinct way to go.

Pericranium answered 31/3, 2022 at 8:10 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.