mouseover function on 3 elements only functions on the last element - javascript

I am a newbie in javascript, so be aware. I already searched on this website for a solution of this issue but I was not able to find the working solution.
The problem is on my development website: https://famnabuurs.nl (it is on the homepage). I use Wordpress with DIVI and now some javascript.
The three columns I created have a similar setup. The columns all have an id so I can manage the behaviour. When you hover over a column some changes should be initiated: background-color of the column should switch to black, stripe background should change from blue to black and the image in the background should grow. That all works for for an individual column. But now I have 3 columns it only works at the last one. What I saw form other issues on this website it will have to do with the definition of my variables, but no idea where to update my script.
My html looks like this, the same for every column except for the id:
<div id='research'>
<div class='textblock'>
.. my text ...
</div>
<div class='myimage koek-achtergrond'>
.. my background image ...
</div>
<div class='myimage koek-stripe'>
.. my other image ...
</div>
</div>
Here is my generic code:
<script>
function mouseover() {
console.log('mouse over this column: ',this.id);
var column = document.getElementById(this.id);
column.style.backgroundColor = 'black';
var stripe = document.getElementById(this.id).getElementsByClassName('koek-stripe')[0];
stripe.classList.add('koek-stripe-hovered');
var background = document.getElementById(this.id).getElementsByClassName('koek-achtergrond')[0];
background.classList.add('koek-transform');
}
function mouseleave() {
console.log('mouse leaves this column: ',this.id);
var column = document.getElementById(this.id);
column.style.backgroundColor = 'var(--primary-blue-color)';
var stripe = document.getElementById(this.id).getElementsByClassName('koek-stripe')[0];
stripe.classList.remove('koek-stripe-hovered');
var background = document.getElementById(this.id).getElementsByClassName('koek-achtergrond')[0];
background.classList.remove('koek-transform');
console.log('classList after leaving: ',background.classList);
}
</script>
In every column I add a short script, where only the columname is changed, in line with the name in the html:
<script>
var columnname = 'research';
columnElement = document.getElementById(columnname);
columnElement.onmouseover = function() {
columnElement.addEventListener('mouseover', mouseover);
}
columnElement.onmouseleave = function() {
columnElement.addEventListener('mouseleave', mouseleave);
}
console.log('kolomdetails: ',columnElement);
</script>
In my opinion it is not relevant to show the css in this case, since the issue refers to the javascript. The problem is that the classes that should be added in the generic script only are applied in the last column. The question is: why do they not remain in de first two columns?
I saw some remarks in other threads indicating it is the classical error in Javascript. So I hope this can help me understand variables in a better way.

Consider using event delegation. Here's a minimal reproducable example to work with:
[`mouseover`, `mouseout`].forEach(h => document.addEventListener(h, handle));
function handle(evt) {
const el2Handle = evt.target.closest(
[`.textblock`, `.koek-achtergrond`, `.koek-stripe`]
.find(c => evt.target.closest(c)) );
if (el2Handle) {
el2Handle.style.color = evt.type === `mouseover` ? `red` : ``;
}
}
#research div {
cursor: pointer;
}
<div id='research'>
<div class='textblock'>
.. my text ...
</div>
<div class='myimage koek-achtergrond'>
.. my background image ...
</div>
<div class='myimage koek-stripe'>
.. my other image ...
</div>
</div>

Yeesssss, this works fine. I took the answer as pointed out below and tailored it to my final solution:
<script>
document.addEventListener(`mouseover`, handle);
document.addEventListener(`mouseout`, handle);
function handle(evt) {
const origin = evt.target;
if ([`.koek-textblock`, `.koek-achtergrond`, `.koek-stripe`].find( c => origin.closest('.koek-kolom'))) {
kolom = origin.closest('.koek-kolom');
kolom.style.backgroundColor = evt.type === `mouseover` ? 'black' : ``;
stripe = kolom.getElementsByClassName('koek-stripe')[0];
stripe.style.backgroundImage = evt.type === `mouseover` ? "url('https://famnabuurs.nl/wp-content/uploads/2022/10/stripe_zwart.png')": ``;
backgroundimage = kolom.getElementsByClassName('koek-achtergrond')[0];
backgroundimage.style.transform = evt.type === `mouseover` ? "scale(1.2) translate(-80px, 40px)" : ``;
}
}
</script>
I also added the class 'koek-kolom' to the column so I am sure I find the right parent.
If you want you can see the result on https://famnabuurs.nl/afbeelding-hoveren-improved/

Related

How to reset button style in Javascript

function showQuestion(questionAndAnswers) {
const shuffledAnswers = _.shuffle(questionAndAnswers.answers);
questionTag.innerText = questionAndAnswers.question;
shuffledAnswers.forEach(({ text, correct }, i) => {
answerTag[i].innerText = text;
answerTag[i].dataset.correct = correct;
});
}
document.querySelectorAll(".answer").forEach((answer) => {
answer.addEventListener("click", (event) => {
if (event.target.dataset ) {
answer.style.border = "1.5px solid"
}
});
});
function nextQuestion() {
const nextIndex = currentQuestionIndex + 1;
if (nextIndex <= myQuestions.length - 1) {
showQuestion(myQuestions[nextIndex]);
currentQuestionIndex = nextIndex;
} else {
end.style.visibility = "visible";
nxt_question_btn.style.visibility = "hidden";
}
}
Basically, In this quiz app, I have 4 buttons for answers and once you click on one answer it makes the border black. The problem I am facing is that once I press the next question, it loads up another question with 4 different answers but one of the buttons will still have the border black. How do I get it to reset once I load up another question? and Extra question if it's okay, how can I only select one button at a time per question?
There is no 'reset' as there is no default, you will just have to manually undo what you did earlier, i.e to remove the border completely:
answer.style.border = "none";
To select each button individually, you will ave to give them each an ID, based of something like an iteration, instead of trying to select them by the shared class
that's a tough one, there is no easy answer without knowing what the previous style was, so it's good to store the previous value of the style in memory and reset the styles to the previous value after the next question has been loaded
// a variable declared somewhere in common scope
let prevBorder
// "backup" the old value when you want to mark the answer as "selected"
prevBorder = element.styles.border
// restore to the initial value when you want to reset the styles
element.styles.border = prevBorder
Maybe you are looking for css:initial property?
The initial CSS keyword applies the initial (or default) value of a property to an element. It can be applied to any CSS property. This includes the CSS shorthand all, with which initial can be used to restore all CSS properties to their initial state.
Or you could add class and use classList.toggle() to switch between them.
I don't have your html code and full code so can't help you fully, but this is an example that may help you implement to your code:
document.querySelectorAll('button').forEach(item => {
item.addEventListener('click', function() {
item.style.border = '10px solid black'
document.querySelectorAll('button').forEach(i => {
if (i != item)
i.style.border = "initial"
})
})
})
<button>Click</button>
<button>Click</button>
<button>Click</button>
<button>Click</button>

How to change button background color by name using Javascript

I have several buttons on my webpage and want to style the background colors when I click on them according to the button's name. I want all the "Toms" to be lightgray, all the "Dicks" to be lightgreen, and all the rest ("Harrys") to be lightyellow. I added the following code to my external Javascript page, but it's not working:
function bgColor() {
var x = document.getElementsByTagName("button").name;
if (x == "Tom") {
document.getElementsByTagName("button").style.backgroundColor=
"lightgray";
} else if (x == "Dick") {
document.getElementsByTagName("button").style.backgroundColor=
"lightgreen";
} else {
document.getElementsByTagName("button").style.backgroundColor=
"lightyellow";
}
}
The HTML reads something like this but between less than/greater than symbols, of course:
button type="button" name="Tom" onclick="bgColor()"
button type="button" name="Dick" onclick="bgColor()"
button type="button" name="Harry" onclick="bgColor()"
EDITED TO ADD
I can't figure out how to reply to Mikkel's comment directly. I tried simply posting another comment, but it wouldn't let me add code. Anyway, I tried the fix that he suggested using the following but it didn't work for me either.
function bgColor() {
var tom = document.querySelector('button[name="Tom"]')
.style.backgroundColor = 'lightgray';
var dick = document.querySelector('button[name="Dick"]')
.style.backgroundColor = 'lightgreen';
var harry = document.querySelector('button[name="Harry"]')
.style.backgroundColor = 'lightyellow';
}
What am I doing wrong?
As #Mikkel has said, your selector is wrong - You'll want to use an attribute selector to get the elements that match your name attribute
However, in this specfic case as well you've got a more weird issue - You'll have to change the name of your function - bgColor is also the name of a property on the elements which I believe is causes your further issues. - You can read more about that here
If you change the name to something like changeColors you shouldn't have this issue
As an aside, there's no need to assign your querySelectors to a variable, you can just run them in this case
function changeColors() {
document.querySelector('button[name="Tom"]').style.backgroundColor = 'lightgray';
document.querySelector('button[name="Dick"]').style.backgroundColor = 'lightgreen';
document.querySelector('button[name="Harry"]').style.backgroundColor = 'lightyellow';
}
<button type="button" name="Tom" onclick="changeColors()"> X </button>
<button type="button" name="Dick" onclick="changeColors()"> X </button>
<button type="button" name="Harry" onclick="changeColors()"> X </button>
document.getElementsByTaName("button") is getting all your buttons. What you want to do, is check each of the buttons individually and apply the color.
You can do it with the code below, that'll change the color of "Tom". You can repeat it to make it work for your other buttons.
function bgColor() {
var tom = document.querySelector('button[name="Tom"]').style.backgroundColor = 'purple'
}
First, using bgColor as a user-defined function name will throw a type error since it's a reserve keyword for JS (Object's property) though deprecated.
Second -> var x = document.getElementsByTagName("button").name; will obviously throw an error x is undefined because this syntax without '.name' should return all buttons on the page in an array, with that you can loop through and access properties of individual button eg. name
So we use this instead var x = document.getElementsByTagName("button") which returns an array of buttons on the page.
That being said, let's see how we can modify your code to achieve what you're looking for:
HTML:
<button type="button" name="Tom" onclick="buttonBgColor(event)"> Tom</button>
<button type="button" name="Dick" onclick="buttonBgColor(event)"> DicK </button>
<button type="button" name="Harry" onclick="buttonBgColor(event)"> Harry </button>
JS:
function buttonBgColor(e) {
const buttons = document.getElementsByTagName("button").name;
for(button of buttons){
const current_button = e.target;
if(current_button.name == 'Tom'){
current_button.style.backgroundColor = 'lightgray'
}else if(current_button.name == 'Dick'){
current_button.style.backgroundColor = 'lightgreen'
}else if(current_button.name == 'Harry'){
current_button.style.backgroundColor = 'lightyellow'
}
}
}
This method is useful because assuming you have two buttons with same name say 'Tom' and yet you want one of them to do something extra aside changing their background colors, you can reference their ID to achieve that using e.target.id or current_button.id in a condition to wire up another handler for that particular 'Tom' button.
Hope this helps.

Swapping images back and forth on click in multiple places

I'm creating an accordion with my own arrow icons (pngs) as the "toggle" so when you click on the down red arrow, the up blue arrow will show to collapse, and vice versa.
I got it to work with the below code, but I have multiple accordions with the same arrow icons, and I need them all to do this. When I add the same code to the other accordions (even if I changed out the ID to be unique and update it in the JS), it still only wants to toggle the first accordion.
Can anyone help me get this to work across multiple image sets (but the same images)?
HTML:
<img src="/wp-content/uploads/2019/08/accordion-open.png" alt="accordion icon" id="accordion" onclick="change();"></div>
JS:
var image_tracker = 'open';
function change(){
var image = document.getElementById('accordion');
if(image_tracker=='open'){
image.src='/wp-content/uploads/2019/08/accordion-close.png';
image_tracker='close';
}
else{
image.src='/wp-content/uploads/2019/08/accordion-open.png';
image_tracker='open';
}
}
If you're assigning the listener by ID it's only going to apply to the first one. Try using a class name instead.
<html>
<body>
<img class="accordion_icon" src="/wp-content/uploads/2019/08/accordion-open.png" />
<script type="text/javascript">
var open_src = "/wp-content/uploads/2019/08/accordion-open.png";
var close_src = "/wp-content/uploads/2019/08/accordion-close.png";
let accordion_icons = document.getElementsByClassName('accordion_icon'); // get all icons img tags
for (let i = 0; i < accordion_icons.length; i++) {
const element = accordion_icons[i];
element.addEventListener('click',(event)=>{ // set listener on each one
console.log('src was: ', event.currentTarget.src);
event.currentTarget.src = (event.currentTarget.src == open_src ? close_src : open_src) // change the src to the one it currently isn't
console.log('scr is now: ', event.currentTarget.src);
});
}
</script>
</body>
</html>
An alternative method could be also this one, if you still want to keep the even handler in the HTML:
<img src="/wp-content/uploads/2019/08/accordion-open.png" alt="accordion icon" id="accordion" onclick="change(this);"></div>
And in the JS:
function change(image){
image.src = image.src.endsWith("open.png")
? image.src.replace(/open\.png$/, "close.png")
: image.src.replace(/close\.png$/, "open.png");
}

Change color of all elements in class on click JavaScript

I have an image (SVG) of a human body. I would like to use JavaScript so that when I click a particular area (say, the lower leg) then all of the elements with the class "lower-leg" (even if not clicked) have their color changed -- this makes it much easier for the user.
Here is the JavaScript I currently have:
function changeclassstyle() {
var c = document.getElementsByClassName("lower-leg");
for (var i=0; i<c.length; i++) {
c[i].style.fill = "red";
}
}
The problem with this code is that it is only generalized for "lower-leg". I may have over a dozen classes I would like this to work for and don't think it is efficient to write 12 functions with the only change being the class name. Is there a way to grab what class was selected and then input that in the function?
--
Additionally, I would love to figure out how, once that section of the body is selected, I can store the class name. I would, in the end, want to store the selection, along with other inputted information in a database. But, this may be for a future question unless someone can help!
Here's how I would do it (tested on a couple of div's).
What we're doing is passing the event object to the event handler (your changeclassstyle() function). It then uses the class of the clicked-on item (the event target's class) and changes everything else on that page with that same class name to use your new desired CSS style.
function changeclassstyle(e) {
// Get all items that have the same class as the item that was clicked
var limbs = document.getElementsByClassName(e.target.className); // for div's and the like
// var limbs = document.getElementsByClassName(e.target.className.baseVal); // turns out this is needed for SVG items
// "limbs" is an HTMLCollection, not an array, so functions like .foreach won't work; C-style for-loops or modern for/let/of loops are better
for (let item of limbs) {
item.style.backgroundColor = 'red';
// item.style.fill = 'red'; // This is probably what you need for your SVG items
}
// You could still use your C-style for loop if needed/wanted
/*
for (var i=0; i<limbs.length; i++) {
limbs[i].style.fill = "red";
}
*/
}
The onchange call looks like this (using my div as the example):
<div class="upper-arm" onclick="changeclassstyle(event)">
</div>
<div class="lower-leg" onclick="changeclassstyle(event)">
</div>
The whole example with simple div's.
<html>
<head><title>stuff</title></head>
<body>
<script type="text/javascript">
function changeclassstyle(e) {
// For debugging. You may want to expand 'e' here in your browser's debug tools if you're not seeing the values you need/want
console.log(e)
var limbs = document.getElementsByClassName(e.target.className.baseVal);
for (let item of limbs) {
item.style.backgroundColor = 'red';
}
}
</script>
<style type="text/css">
div {
height: 100px;
width: 100px;
background-color: 'white';
border: 1px solid black;
}
</style>
<div class="upper-arm" onclick="changeclassstyle(event)">
</div>
<div class="upper-arm" onclick="changeclassstyle(event)">
</div>
<div class="upper-arm" onclick="changeclassstyle(event)">
</div>
<div class="lower-leg" onclick="changeclassstyle(event)">
</div>
<div class="lower-leg" onclick="changeclassstyle(event)">
</div>
<div class="lower-leg" onclick="changeclassstyle(event)">
</div>
</body>
</html>
You can use parameters in function where you pass class and color like below
function changeStyle(cls,clr) {
let elems = document.getElementsByClassName(cls);
if(!elems) return;
for (let elem of elems) {
elem.style.color = clr;
}
}
As per the iteration of many classes like i said you can store classes in array and iterate each of them.
let classes = ['one','two','three','four'];
classes.forEach(function (cls) {
changeStyle(cls,"red");
});
You can play with fiddle here if you want to test/experiment: https://jsfiddle.net/thrL5uqw/8/
Note: Change style property as you wish, For now i have used color for demo
I'm a bit late to the party, but here's my take on the problem.
Like the others told you, you'll need to use an additional parameter to your function to specify the class you want to modify your elements (or try to figure out the class from the clicked element), therefore you should have something like that:
/**
* This function will handle the click event on one of the part of the SVG.
* #param {string} lClass This the class of the element to modify
*/
function handleClick(lClass) {
for (let e of document.getElementsByClassName(lClass)) {
// Here you can do all the changes you need on the SVG element.
e.style.fill = "red";
}
}
And when it comes to the event binding, you could do like the other suggested and add the onclick event binding propery on the HTML Element, or you could bind it in you JS with the addEventListener function (that way you don't have to repeat the onclick property on each of your SVG elements).
// For each element of all the listed class, bind the "click" event to the handleClick function
const listenClass = [/*List of your classes*/];
for (let l of listenClass) {
for (let e of document.getElementsByClassName(l)) {
e.addEventListener('click', handleClick.bind(this, l));
}
}
Demo: https://plnkr.co/edit/gay2yBaVi5QD868fsTa6?p=preview
I hope it helped.

Javascript if else statement to hide and show div

Please refer to the following codes :
<div id="message-1" onclick="javascript:showresponddiv(this.id)>
</div>
<div id="respond-1" style="display:none;">
</div>
<div id="message-2" onclick="javascript:showresponddiv(this.id)>
</div>
<div id="respond-2" style="display:none;">
</div>
<script type="text/javascript">
function showresponddiv(messagedivid){
var responddivid = messagedivid.replace("message-", "respond-");
if (document.getElementById(responddivid).style.display=="none"){
document.getElementById(responddivid).style.display="inline";
} else {
document.getElementById(responddivid).style.display="none";
}
}
</script>
The codes above already success make the respond div appear when user click on message div. The respond div will disappear when user click on message div again. Now my question is how to make the respond div of 1st message disappear when user click on 2nd message to display the respond div of 2nd message?
You should give the "respond" divs a common class:
<div id="respond-1" class="response' style="display:none;"></div>
Then you can get all divs by using getElementsByTagName, compare the class and hide them on a match:
function hideAllResponses() {
var divs = document.getElementsByTagName('div');
for(var i = divs.length; i-- ;) {
var div = divs[i];
if(div.className === 'response') {
div.style.display = 'none';
}
}
}
We cannot use getElementsByClassName, because this method is not supported by IE8 and below. But of course this method can be extended to make use of it if it is supported (same for querySelectorAll). This is left as an exercise for the reader.
Further notes:
Adding javascript: to the click handler is syntactically not wrong but totally unnecessary. Just do:
onclick="showresponddiv(this.id)"
If you have to do a lot of DOM manipulation of this kind, you should have a look at a library such as jQuery which greatly simplify such tasks.
Update: If always only one response is shown and you are worried about speed, then store a reference to opened one:
var current = null;
function showresponddiv(messagedivid){
var id = messagedivid.replace("message-", "respond-"),
div = document.getElementById(id);
// hide previous one
if(current && current !== div) {
current.style.display = 'none';
}
if (div.style.display=="none"){
div.style.display="inline";
current = div;
}
else {
div.style.display="none";
}
}
Edit: Fixed logic. See a DEMO.
You can add some class to all divs with id="respond-"
e.g
<div id="respond-1" class="classname" style="display:none;"></div>
<div id="respond-2" class="classname" style="display:none;"></div>
Now at first row of your function "showresponddiv()" you should find all divs with class "classname" and hide them.
With jQuery it is simple code:
$(".classname").hide();
jQuery - is a Javascript Library that helps you to easy manipulate with DOM and provides cross-browser compatibility.
Also you can look to Sizzle - it is a JavaScript CSS selector engine used by jQuery for selecting DOM elements

Categories