1

I've tried to add two event listeners into my external script, both of which worked fine as event attributes attached to HTML elements.

The event listeners are:

parent.addEventListener("mousemove", where(event));
parent.addEventListener("mouseleave", reset(event));

The rest of the code can be seen here: https://codepen.io/rowancavanagh/pen/NBmPGv Or here in the snippet:

window.onload = function() {
 var parent = document.querySelectorAll(".shadowed");
 var shadow = document.createElement("div");
 for (var i = 0; i < parent.length; i++) {
   parent[i].appendChild(shadow);
 }
 parent.addEventListener("mousemove", where(event));
 parent.addEventListener("mouseleave", reset(event));
}

function where(e) {
 var x = e.offsetX / e.target.offsetWidth * 10 - 5;
 var y = e.offsetY / e.target.offsetHeight * 10 - 5;
 e.target.querySelector('.shadowed div').style.transform = 'translateX(' + x + 'px) translateY(' + y + 'px)';
}

function reset(e) {
 e.target.querySelector('.shadowed div').style.transform = 'translateX(0px) translateY(0px)';
}
* {
 margin: 0;
 padding: 0;
 box-sizing: border-box;
}

html, body {
 height: 100%;
}

body {
 background-color: #eee;
 display: flex;
 justify-content: center;
 align-items: center;
}

.shadowed {
 position: relative;
 width: 400px;
 height: 240px;
 margin: 10px;
 background-color: #fff;
 border-radius: 5px;
}

.shadowed div {
 position: absolute;
 bottom: 0;
 right: 0;
 margin: 0 -10px -10px 0;
 width: calc(100% - 40px);
 height: calc(100% - 40px);
 background-color: #ddd;
 border-radius: 5px;
 z-index: -1;
 transform: translateY(0px) translateX(0px);
 transition: transform .1s ease-out;
}
<div class="shadowed"></div>

As you can see, they should be firing a couple of functions on mousemove and mouseleave, but aren't.

Any ideas what I'm doing wrong?

Calvin Nunes
  • 6,376
  • 4
  • 20
  • 48
Rowan
  • 485
  • 3
  • 5
  • 13
  • you can't attach a listener to an array of objects. (`querySelectorAll` returns an array with all elements found, even if is just one). You can move the attachment of the listener to inside the loop you have just above, using `parent[i].addEv...` – Calvin Nunes Aug 16 '18 at 21:26
  • Didn't you see the error message `Uncaught TypeError: parent.addEventListener is not a function`? – Barmar Aug 16 '18 at 21:44

4 Answers4

2

You are invoking the function as soon as you are binding the event. The event handler needs a function reference.

parent.addEventListener("mousemove", where(event));

is supposed to be

parent.addEventListener("mousemove", where);

Also the parent var contains a live node list of all the elements with that particular class. addEventListener is a method available for a specific element that needs to be bound separately.

window.onload = function() {
    var parent = document.querySelectorAll(".shadowed");
    var shadow = document.createElement("div");
    for (var i = 0; i < parent.length; i++) {
      parent[i].appendChild(shadow);
    }

    parent.forEach(function(elem) {
       elem.addEventListener("mousemove", where);
       elem.addEventListener("mouseleave", reset);
    });
}
Sushanth --
  • 55,259
  • 9
  • 66
  • 105
2

parent is a NodeList as you used document.querySelectorAll. You will need to use Array.prototype.forEach or a for loop to add event listeners to each of the elements selected by the query selector. If you only want to match the first element, you can use document.querySelector and add the event listeners in the same way you have already done as it will return the first Element that matches the selector.

Change

window.onload = function() {
    var parent = document.querySelectorAll(".shadowed");
    var shadow = document.createElement("div");
    for (var i = 0; i < parent.length; i++) {
      parent[i].appendChild(shadow);
    }
    parent.addEventListener("mousemove", where(event));
    parent.addEventListener("mouseleave", reset(event));
}

To

window.onload = function() {
    var parent = document.querySelectorAll(".shadowed");
    var shadow = document.createElement("div");
    for (var i = 0; i < parent.length; i++) {
      parent[i].appendChild(shadow);
    }
    parent.forEach(function(item){
      item.addEventListener("mousemove", where);
      item.addEventListener("mouseleave", reset);
    });
}

Also, you only need to give the name of the function to the event listener; you do not need to invoke the function (passing in the event Object).

Change

parent.addEventListener("mousemove", where(event));
parent.addEventListener("mouseleave", reset(event));

To

parent.addEventListener("mousemove", where);
parent.addEventListener("mouseleave", reset);

window.onload = function() {
 var parent = document.querySelectorAll(".shadowed");
 var shadow = document.createElement("div");
 for (var i = 0; i < parent.length; i++) {
   parent[i].appendChild(shadow);
 }
  parent.forEach(function(item){
    item.addEventListener("mousemove", where);
   item.addEventListener("mouseleave", reset);
  });
}

function where(e) {
 var x = e.offsetX / e.target.offsetWidth * 10 - 5;
 var y = e.offsetY / e.target.offsetHeight * 10 - 5;
 e.target.querySelector('.shadowed div').style.transform = 'translateX(' + x + 'px) translateY(' + y + 'px)';
}

function reset(e) {
 e.target.querySelector('.shadowed div').style.transform = 'translateX(0px) translateY(0px)';
}
* {
 margin: 0;
 padding: 0;
 box-sizing: border-box;
}

html, body {
 height: 100%;
}

body {
 background-color: #eee;
 display: flex;
 justify-content: center;
 align-items: center;
}

.shadowed {
 position: relative;
 width: 400px;
 height: 240px;
 margin: 10px;
 background-color: #fff;
 border-radius: 5px;
}

.shadowed div {
 position: absolute;
 bottom: 0;
 right: 0;
 margin: 0 -10px -10px 0;
 width: calc(100% - 40px);
 height: calc(100% - 40px);
 background-color: #ddd;
 border-radius: 5px;
 z-index: -1;
 transform: translateY(0px) translateX(0px);
 transition: transform .1s ease-out;
}
<div class="shadowed"></div>
Unmitigated
  • 76,500
  • 11
  • 62
  • 80
1

parent is the result of running querySelectorAll, so it will be an array of elements. You can move the event listeners into the loop so that all instances of .shadowed will listen for the events.

Another thing is the way you're passing the function to the event listener. parent.addEventListener("mousemove", where(event)); will set the result of the function call to the listener, you just want to pass the actual name of the function. The onload could look something like this:

window.onload = function() {
    var parent = document.querySelectorAll(".shadowed");
    var shadow = document.createElement("div");
    for (var i = 0; i < parent.length; i++) {
      parent[i].appendChild(shadow);
      parent[i].addEventListener("mousemove", where);
      parent[i].addEventListener("mouseleave", reset);
    }
}

after those changes

Unmitigated
  • 76,500
  • 11
  • 62
  • 80
sbrass
  • 905
  • 7
  • 12
1

parent is an array and you can't add EventListeners to an array so you need to add the EventListener to each element of the array inside of the for loop

eg.:

for (var i = 0; i < parent.length; i++) {
  parent[i].appendChild(shadow);
  parent[i].addEventListener("mousemove", where(event));
  parent[i].addEventListener("mouseleave", reset(event));
}
Jose Silva
  • 51
  • 5