0

Having a list of HTML anchors, I need to add Event listener, which as I understand is not recieving array when I mention it as parameter.

As a try, instead of extra functions I used self-calling, but this way even i wasn't listened when debugging.

for (var i=0; i < anc.length; i++) {
    anc[i].addEventListener('mouseover', function(i,pop,tri) {
       console.log('i=%i', i);    // last value of loop
       pop[i].style.display = 'initial';
       tri[i].style.animation='anim_dpt_tr 0.4s ease-out forwards';
    });
    ...
}

A more useful code

var pop = document.querySelectorAll('#cities > li > a > .popup');
const anc = document.querySelectorAll('#cities > li > a');
var tri = document.getElementsByClassName('triangle-left');

for (var i=0; i < anc.length; i++) {
    anc[i].addEventListener('mouseover', over(i, tri, pop));
    anc[i].addEventListener('touchmove', over(i, tri, pop));
    anc[i].addEventListener('touchend', out(i, tri, pop));
    anc[i].addEventListener('mouseout', out(i, tri, pop));
}

function over(i,tri,pop) {
    console.log("over_address=%i", pop[i]); // =0
    pop[i].style.display = 'initial';
    tri[i].style.animation='anim_dpt_tr 0.4s ease-out forwards';
}
function out(i,tri,pop) {
    console.log("out_i=%i", i);             // =each index
    pop[i].style.display = 'none';
    tri[i].style.animation='anim_dpt_tr_back 0.3s ease-in forwards';
}

here HTML tree

<ul id="cities">
  <li>
    <a href="">Shoulder
      <span class="triangle-left"></span> <span class="popup">Arm</span> </a>
  </li>
...

How to pass arrays correctly to Event handler?

  • 1
    IIRC, `.querySelector()` returns a NodeList, NOT an array -- you may need to convert it to an array before doing array type work against the elements. ( https://stackoverflow.com/questions/3199588/fastest-way-to-convert-javascript-nodelist-to-array ) – Doug Apr 02 '19 at 15:06

3 Answers3

3

Your "more useful code" is incorrect because it doesn't actually assign any event handlers; it just invokes the code immediately and returns undefined.

To take that approach, you'd have those functions return a function. That's where you'd define the event parameter.

function over(i,tri,pop) {
  return function(event) {
    console.log("over_address=%i", pop[i]); // =0
    pop[i].style.display = 'initial';
    tri[i].style.animation='anim_dpt_tr 0.4s ease-out forwards';
  }
}
function out(i,tri,pop) {
  return function(event) {
    console.log("out_i=%i", i);             // =each index
    pop[i].style.display = 'none';
    tri[i].style.animation='anim_dpt_tr_back 0.3s ease-in forwards';
  }
}

This is an old-school way of doing it. More recently, you'd use an i variable that's scoped to the for loop's block by using let or const instead of var.


Another thing to consider is if it's really worth it to cache the collections. If there's a pretty simple relationship in the layout between each i element in each collection, then DOM traversal inside the handler may be preferred.


Yet another possibility is to create an object that implements the EventListener interface. Then you can just assign each element to the object, and use the object itself in lieu of an event handler function.

var pop = document.querySelectorAll('#cities > li > a > .popup');
const anc = document.querySelectorAll('#cities > li > a');
var tri = document.getElementsByClassName('triangle-left');

for (var i=0; i < anc.length; i++) {
    // Create a new instance of your object, giving it the data needed
    //   when an event occurs.
    var obj = new MyElements(pop[i], anc[i], tri[i], i);

    // Use the object itself in lieu of an event handler function
    //  (see below)
    anc[i].addEventListener("mouseover", obj);
    anc[i].addEventListener("touchmove", obj);
    anc[i].addEventListener("mouseout", obj);
    anc[i].addEventListener("touchend", obj);
}

// Holds the relevant info for each index
function MyElements(pop, anc, tri, i) {
  this.pop = pop
  this.anc = anc
  this.tri = tri
  this.i = i
}

// Implements the EventListener interface.
// This is what gets invoked when an event occurs.
// It is set up to simply invoke a function on the same object
//   that has been given the same name as the event.
MyElements.prototype.handleEvent = function(event) {
  this[event.type](event)
}

// These are the functions for each event type. For simplicity, I
// gave each handler the name of the event that fires it so that
// you can use event.type to invoke them.
MyElements.prototype.mouseover = 
MyElements.prototype.touchmove = function over(event) {
    console.log("over_address=%i", this.i); // =0
    this.pop.style.display = 'initial';
    this.tri.style.animation='anim_dpt_tr 0.4s ease-out forwards';
}

MyElements.prototype.mouseout =
MyElements.prototype.touchend = function out(event) {
    console.log("out_i=%i",this.i);             // =each index
    pop[i].style.display = 'none';
    tri[i].style.animation='anim_dpt_tr_back 0.3s ease-in forwards';
}
ziggy wiggy
  • 1,039
  • 6
  • 6
0

You could use .bind() to get this done. All your calls could look like, for e.g. over.bind(null, i, tri, anc), when used as a function handler. Notice, a required null as the first argument.

Therefore, that would transform to :

over.bind(null, i, tri, pop)
out.bind(null, i, tri, pop)

Let me know if that works. Else, leave me a comment.

trk
  • 2,106
  • 14
  • 20
0

This line anc[i].addEventListener('anyEvent', over(i, tri, pop)) will try to immediately invoke the function. Instead you can create a callback function and call the over & out from that. Also instead of passing multiple parameter pass the context using this. Here this will refer to the element which is triggering the event that is the anchor tag in this case.

Then inside the over/out function use elem.querySelector('.popup') which will target an element with popup class inside the element which is triggering the event . Then you can add the style properties

const anc = document.querySelectorAll('#cities > li > a');
for (var i = 0; i < anc.length; i++) {
  anc[i].addEventListener('mouseover', function() {
    over(this)
  });

  anc[i].addEventListener('mouseout', function() {
    out(this)
  });
}

function over(elem) {
  elem.querySelector('.popup').style.display = 'initial';
  elem.querySelector('.triangle-left').style.animation = 'anim_dpt_tr 0.4s ease-out forwards';
}

function out(elem) {
  elem.querySelector('.popup').style.display = 'none';
  elem.querySelector('.triangle-left').style.animation = 'anim_dpt_tr_back 0.3s ease-in forwards';
}
<ul id="cities">
  <li>
    <a href="">Shoulder
      <span class="triangle-left"></span> <span class="popup">Arm</span> </a>
  </li>
  <li>
    <a href="">Shoulder
      <span class="triangle-left"></span> <span class="popup">Leg</span> </a>
  </li>
  <li>
    <a href="">Shoulder
      <span class="triangle-left"></span> <span class="popup">ercerc</span> </a>
  </li>
</ul>
brk
  • 48,835
  • 10
  • 56
  • 78
  • That's a Snickers, @brk! You answer even includes a demonstration. I have chosen your variant as answer 'cause is't mostly smart and your explication of methods is more reasonable. – cpu_figther Apr 03 '19 at 08:37