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