1

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()));

3
  • try this: [...btn_primarys].map, in fact you don't need map, just use forEach instead Commented Mar 5, 2021 at 11:53
  • 1
    Does this answer your question? Result of `document.getElementsByClassName` doesn't have array methods like `map` defined, even though it is an array Commented Mar 5, 2021 at 12:35
  • 1
    Unless you need the live HTMLCollection returned by getElementsByClassname prefer querySelectorAll to retrieve a static NodeList. Also, don't use map() unless you need the returned array, instead use forEach() (which is provided as a method on NodeList without having to convert it to an Array). Commented Mar 5, 2021 at 13:13

2 Answers 2

1

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

Sign up to request clarification or add additional context in comments.

1 Comment

Your ideas helped but I'm still getting error. Help me check my code below and how can I resolve it. Thanks
1

@Християн Христов

I already came up with a solution for one instance of two buttons initially before a posted the question. Although I wasn't aware of hoisting before you talked about it but it worked. See my code below.

btn_primary = document.querySelector('.btn-primary');
btn_secondary = document.querySelector('.btn-secondary');

btn_primary.addEventListener("mouseover", () => {
btn_primary.classList.remove("btn-primary");
  btn_primary.classList.add("btn-secondary");
  btn_secondary.classList.add("btn-primary");
  btn_secondary.classList.remove("btn-secondary");

});

btn_primary.addEventListener("mouseout", () => {
  btn_primary.classList.add("btn-primary");
  btn_primary.classList.remove("btn-secondary");

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

 btn_secondary.addEventListener("mouseover", () => {
   btn_secondary.classList.remove("btn-secondary");
   btn_secondary.classList.add("btn-primary");

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

  });

  btn_secondary.addEventListener("mouseout", () => {
    btn_secondary.classList.add("btn-secondary");
    btn_secondary.classList.remove("btn-primary");
 
    btn_primary.classList.add("btn-primary");
    btn_primary.classList.remove("btn-secondary");
   });
 .btn-primary{
   background: red;
  }
 
 .btn-secondary{
   background: orange;
  }
  <button class="btn btn-primary"> Button 1 </button>

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

If you run it, you will see my result and how I actually wanted it to work. The colors are interchanging on mouseover and mouseout. But where the problem lies is working with multiple instance of the two buttons. I came up with a solution using idea of the hoisting and array.prototype.forEach but I wasn't getting satisfied result. Its only working partially. I would be glad if you can help me review it.

See the code below

const mouseOverBtnPrimary = (btn_primary, btn_secondary) => {
    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, btn_secondary) => {
    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_primary, btn_secondary) => {
    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_primary, btn_secondary) => {
    btn_secondary.classList.add("btn-secondary");
    btn_secondary.classList.remove("btn-primary");

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

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

const btn_primarys = projectPage.getElementsByClassName('btn-primary');
[].forEach.call(btn_primarys, (btn_primary) => {
    btn_primary.addEventListener('mouseover', (e) => {
        mouseOverBtnPrimary(e.target)
    })
});

[].forEach.call(btn_primarys, (btn_primary) => {
    btn_primary.addEventListener('mouseout', (e) => {
        mouseOutBtnPrimary(e.target)
    })
});

const btn_secondarys = projectPage.getElementsByClassName('btn-secondary');
[].forEach.call(btn_secondarys, (btn_secondary) => {
    btn_secondary.addEventListener('mouseover', (e) => {
        mouseOverBtnSecondary(e.target)
    })
});

[].forEach.call(btn_secondarys, (btn_secondary) => {
    btn_secondary.addEventListener('mouseout', (e) => {
    mouseOutBtnSecondary(e.target)
    })
});

1 Comment

Can your extend your original question with this, and i will try to look in to it latter

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.