0

I am trying to set a style event for a multiple images tags and I can't: the error is in the anonymous function of the event when I put into a loop to make the effect individually and for all galleries in the document with images[i].

Why ? : Uncaught TypeError: Cannot set property 'style' of undefined at HTMLImageElement.<anonymous>

images = document.querySelectorAll(".gallery img");

for (var i=0; i<images.length; i++){
    images[i].addEventListener('mouseout', function(){ images[i].style=" transform: scale(.8)" }); 
    images[i].addEventListener('mouseover', function(){ images[i].style=" transform: scale(1.2)" }); 
} 
<div class="gallery">
    <img src="https://picsum.photos/300/180">
    <img src="https://picsum.photos/300/180">
    <img src="https://picsum.photos/300/180">
</div>
  • 1
    See [this answer](https://stackoverflow.com/questions/750486/javascript-closure-inside-loops-simple-practical-example), it covers similar problem, and all solutions apply to your case too. Easy one line answer: replace `var` with `let`. – alx Sep 04 '22 at 15:26

4 Answers4

0

ES6+ solution:

Simply change var to let in the for loop.

images = document.querySelectorAll(".gallery img");

for (let i = 0; i < images.length; i++){
    images[i].addEventListener('mouseout', function(){ images[i].style=" transform: scale(.8)" }); 
    images[i].addEventListener('mouseover', function(){ images[i].style=" transform: scale(1.2)" }); 
} 
<div class="gallery">
    <img src="https://picsum.photos/300/180">
    <img src="https://picsum.photos/300/180">
    <img src="https://picsum.photos/300/180">
  </div>

If you must use var rather than let, you can use .bind() to set the value of i for the function. Otherwise it will reference the last value of i in the loop because of the async nature of the event callback and the higher scope of i due to the use of var.

images = document.querySelectorAll(".gallery img");

function callback1(i){ images[i].style=" transform: scale(.8)" };
function callback2(i){images[i].style=" transform: scale(1.2)" };

for (var i=0; i<images.length; i++){
    images[i].addEventListener('mouseout', callback1.bind(this, i));
    images[i].addEventListener('mouseover', callback2.bind(this, i)); 
}
  <div class="gallery">
    <img src="https://picsum.photos/300/180">
    <img src="https://picsum.photos/300/180">
    <img src="https://picsum.photos/300/180">
  </div>
WillD
  • 5,170
  • 6
  • 27
  • 56
0

Use forEach instead of a for loop:

images.forEach(i=> {
    i.addEventListener('mouseout', function() { i.style=" transform: scale(.8)" });
    i.addEventListener('mouseover', function() { i.style=" transform: scale(1.2)" });
}) 
SuperStormer
  • 4,997
  • 5
  • 25
  • 35
0

You should essentially never use var in Javascript but use let instead. var has weird scoping rules and should be removed from the language, the only reason it's still there is backwards compatibility.

But you should just always use let (or const) and it will work as expected.

Guillaume Brunerie
  • 4,676
  • 3
  • 24
  • 32
0

Use arrow functions in callback for events to have right context (this)

images = document.querySelectorAll(".gallery img");

images.forEach((img) => {
  img.addEventListener("mouseout", () => {
    img.style = " transform: scale(.8)";
  });
  img.addEventListener("mouseover", () => {
    img.style = " transform: scale(1.2)";
  });
});
<div class="gallery">
    <img src="https://picsum.photos/300/180">
    <img src="https://picsum.photos/300/180">
    <img src="https://picsum.photos/300/180">
</div>
Siva K V
  • 10,561
  • 2
  • 16
  • 29