Skip to content
Advertisement

How can I call function on event listener to change class using map method

I have two buttons, button 1 and button 2 of different classes with different color and background color. When mouseover button 1 I want the color and background color to change to the color and background color of button 2 and and the color and background color of button 2 to change to the color and background color of button 1. likewise when I hover button 2 the same should happen.

So I came up the this solution but I was being notified

Uncaught TypeError: btn_primarys.map is not a function

Uncaught TypeError: btn_secondarys.map is not a function

const mouseOverBtnPrimary = () => {
    btn_primary.classList.remove("btn-primary");
    btn_primary.classList.add("btn-secondary");

    btn_secondary.classList.add("btn-primary");
    btn_secondary.classList.remove("btn-secondary");

}

const mouseOutBtnPrimary = () => {
    btn_primary.classList.add("btn-primary");
    btn_primary.classList.remove("btn-secondary");

    btn_secondary.classList.add("btn-secondary");
    btn_secondary.classList.remove("btn-primary");
}

const mouseOverBtnSecondary = () => {
    btn_secondary.classList.remove("btn-secondary");
    btn_secondary.classList.add("btn-primary");

    btn_primary.classList.add("btn-secondary");
    btn_primary.classList.remove("btn-primary");
}

const mouseOutBtnSecondary = () => {
    btn_secondary.classList.add("btn-secondary");
    btn_secondary.classList.remove("btn-primary");

    btn_primary.classList.add("btn-primary");
    btn_primary.classList.remove("btn-secondary");
}

let projectPage = document.getElementById('projectsPage');

let btn_primarys = projectPage.getElementsByClassName('btn-primary');
btn_primarys.map( btn_primary => btn_primary.addEventListener('mouseover', mouseOverBtnPrimary()));
btn_primarys.map( btn_primary => btn_primary.addEventListener('mouseout', mouseOutBtnPrimary()));


let btn_secondarys = projectPage.getElementsByClassName('btn-secondary');
btn_secondarys.map( btn_secondary => btn_secondary.addEventListener('mouseover', mouseOverBtnSecondary()));
btn_secondarys.map( btn_secondary => btn_secondary.addEventListener('mouseout', mouseOutBtnSecondary()));

Advertisement

Answer

There are few issues with your code.

First in your functions responsible for adding and removing classes you are referring variables that are not yet declared. When using let and const your variables are not being hoisted the same way as when using var, so even if you fix the map error you will be still in trouble.

About the map error, when querying the DOM the browser API-s are returning HTML collection which is an array like collection but it is not an array, so that is why the map method doesn’t exist on the collection.

There are two ways in which you can resolve this issue: First use the Array.from() method, which in your case will look like so

Array.from(btn_primarys).map(x => {...})

Another way to use the map method is to use it via the Array.prototype like so:

Array.prototype.map.call(btn_primarys, (x => x))

About the solution itself you actually don’t need to use arrays as there will be only one instance of the two buttons, so it would suggest you to try doing something like the snippet below. In the snippet below I’m using the document.querySelector which will return me single element (so no array-like stuff is needed).

The other important thing (and different from your solution) is that I’m passing reference pointing at the button i want to change as a function argument (this solves the issue with hoisting that i mentioned above)

const buttonOne = document.querySelector('.first-button');
const buttonTwo = document.querySelector('.second-button');

const setPrimaryClass = (btn) => {
  btn.classList.add('primary-button');
  btn.classList.remove('secondary-button');
}

const setSecondaryClass = (btn) => {
  btn.classList.add('secondary-button');
  btn.classList.remove('primary-button');
}


buttonOne.addEventListener('mouseover', (e) => {
  setSecondaryClass(e.target)
})
buttonOne.addEventListener('mouseout', (e) => {
  setPrimaryClass(e.target)
})

buttonTwo.addEventListener('mouseover', (e) => {
  setPrimaryClass(e.target)
})
buttonTwo.addEventListener('mouseout', (e) => {
  setSecondaryClass(e.target)  
})
.primary-button{
  background:red;
}

.secondary-button{
  background: orange;
}
<button class="first-button primary-button"> Button 1 </button>

<button class="second-button secondary-button"> Button 2 </button>

Here is a link to MDN about HTML collections And here is a link about Hoisting i hope that this answers will solve your problem.

p.s. As i see that you are new to stackOverflow community, you can click on the Run code snippet button above and see a demo of my code.

EDIT

Multiple button case

const buttonOne = document.querySelectorAll('.first-button');
const buttonTwo = document.querySelectorAll('.second-button');

const setPrimaryClass = (btn) => {
  btn.classList.add('primary-button');
  btn.classList.remove('secondary-button');
}

const setSecondaryClass = (btn) => {
  btn.classList.add('secondary-button');
  btn.classList.remove('primary-button');
}

buttonOne.forEach(button => button.addEventListener('mouseover', (e) => {
  setSecondaryClass(e.target)
}))
buttonOne.forEach(button => button.addEventListener('mouseout', (e) => {
  setPrimaryClass(e.target)
}))


buttonTwo.forEach(button => button.addEventListener('mouseover', (e) => {
  setPrimaryClass(e.target)
}))
buttonTwo.forEach(button => button.addEventListener('mouseout', (e) => {
  setSecondaryClass(e.target)  
}))
.primary-button{
  background:red;
}

.secondary-button{
  background: orange;
}
<div>
<button class="first-button primary-button"> Button 1 </button>
<button class="first-button primary-button"> Button 1 </button>
<button class="first-button primary-button"> Button 1 </button>
<button class="first-button primary-button"> Button 1 </button>
<button class="first-button primary-button"> Button 1 </button>
</div>

<div>
<button class="second-button secondary-button"> Button 2 </button>
<button class="second-button secondary-button"> Button 2 </button>
<button class="second-button secondary-button"> Button 2 </button>
<button class="second-button secondary-button"> Button 2 </button>
<button class="second-button secondary-button"> Button 2 </button>
</div>

In the example above you can see how i achieved the desired result with multiple buttons. My advice for cases where you have multiple elements dispatching events (e.g. you need a lot of event listeners in the same view) to try and use the following technique Event Delegation

Capturing and bubbling allow us to implement one of most powerful event handling patterns called event delegation. The idea is that if we have a lot of elements handled in a similar way, then instead of assigning a handler to each of them – we put a single handler on their common ancestor. In the handler we get event.target to see where the event actually happened and handle it.- javascript.info

Advertisement