Handling Events for Many Elements

Here is a working (I’m guessing this is what you want):

https://jsfiddle.net/n7ukn6av/5/

If anything is confusing, just ask :slight_smile:

That sir, is some crazy lean JavaScript. The frameworks seem to bulk things up a bit and not allow you to understand core JS.

Kudos to you. I shall study the code, commit to memory and refactor OO (prototype) style so I can re-use it over & over.

I would have explained it more yesterday, but I had a meeting. I included some comments to help explain things more clearly:

https://jsfiddle.net/n7ukn6av/6/

Thank you so much, I am going to attempt to add in the ability to accept min and max values.

function updateFrom(button) { <<< so button is just a name for any argument that could be there that targets the button you select?

yup. Its a custom function I made up on my own and I picked the name button though I could have called it anything else as well. The value of button comes from the click handler. But because this function is defined this way, we could call it from anywhere, not just a click handler, as long as we had a reference to the button we wanted to calculate an update from.

so its the same as saying:

“< div onclick=“cliker(x)”>”

function clicker(x) {
//event listener targeting THIS elem
}

Not exactly the same. clicker in your example is still being treated as an event handler - something specific to events, accepting a single argument that is from an event. It doesn’t work well in contexts other than being an event handler. updateFrom is more generic. It can be called anywhere at any time and doesn’t rely on an event. This makes it more versatile than any other, ordinary event handler.

That said, in onclick attributes on html, you’re just running arbitrary code that is wrapped in an event handler. So technically clicker doesn’t have to be an event handler. It can ignore the event argument and just be a generic function too. But most of the time when you’re writing attribute code like that, its in the form of a handler specific to that attribute (e.g. a click handler for the onclick event)

2 Likes

By the way, something interesting you can do is write an attribute handler and then check the handler property of that element in javascript. For example:

<a onclick="alert(this.nodeName)">Click Me</a>
var a = document.getElementsByTagName('a')[0];
console.log(a.onclick); // =>
/*
function onclick(event) {
  alert(this.nodeName)
}
*/

Here you can see the wrapper handler that goes around the code that was put in the onclick attribute. Its a single-argument function with a parameter named event. That function is also called in the context of that element so this in that call is the element itself.

1 Like

Nice to see you joined kforums @joe_hoeller, I remember when I would look @senocular code back in the 90s and think how stupid I was. Nothing has changed LOL

3 Likes

@grimdeath - one of my most favoritest people! :love: :love: :love:

The feelings mutual master :yoda:

Group hug!!! :hamburger:

I’m still confused. What if I have different buttons/links with different parents throughout my entire site that each have a different HTML class/id, but all use the same event listener. Would this code still work?

Example:
var button1 = document.querySelector(’#parent1 .button’).addEventListener…;
var button2 = document.querySelector(’#parent2 .button’).addEventListener…;
var button3 = document.querySelector(’#parent3 .button’).addEventListener…;

I don’t have access to modifying the HTML either.

@TallGlassOfMike That still works. The approach described in this tutorial doesn’t work as well in that case, so maybe you’ll want to assign them individually like that.

Otherwise it would mean a more complicated filter. In using a single event listener, you need to be able to tell that what you clicked was what you wanted to be clicked. This was what if (e.target !== e.currentTarget) { ... } was doing - making sure that clicks that were made on #theDude (e.currentTarget) but not any of the one, two, three, four, or five buttons (e.target) were getting ignored. If the background of #theDude was clicked, for example, then e.target === e.currentTarget would be true.

If you have multiple buttons in different hierarchies, then the single handler would have to be applied to the common parent of those buttons. In a worst case scenario, that would be the body element. But then you would need to check e.target for basically every click made in the entire document to see if it’s one of your buttons. The check for this wouldn’t actually be too hard if you had a list of selectors targeting your buttons. It could be as simple as:

document.body.addEventListener('click', function (e) {
    if (e.target.matches('#parent1 .button, #parent2 .button, #parent3 .button')) {
        // do something with your clicked button
    }
});

But there is the downside of this check happening for every click in the common parent. If you’re ok with that (does one if check really matter that much?) then this could work for you. One event handler is a lot easier to manage than multiple event handlers and it means the dom can be changed and have .button elements added and removed and re-added without affecting the click handler at all.

1 Like

Excellent post, thanks! How would this be done for contenteditable div tags? It looks like this:

<div id="theParent" contenteditable="true"></div>
    <div class="el"...></div>
    <div class="el"...></div>
    ...

With JavaScript:

var e = document.querySelector("#theParent");
e.addEventListener("input", doSomething, false);

When I run this, it returns e.target as “theParent”. I tried adding contenteditable to each of the inner div tags as well, which didn’t work. Would greatly appreciate any help!

If you replace the input event with click, then the appropriate element you clicked on works - even if the parent is a contenteditable div element:

<body>
  <div id="theParent" contenteditable="true">
    <div id="foo" class="el">A</div>
    <div id="bar" class="el">B</div>
  </div>

  <script>
    var theParent = document.querySelector("#theParent");
    theParent.addEventListener("click", doSomething, false);

    function doSomething(e) {
      if (e.target !== e.currentTarget) {
          var clickedItem = e.target.id;
          console.log("Hello " + clickedItem);
      }
      e.stopPropagation();
    }
  </script>

The input event is a bit bizarre in how it behaves. Can you share more on what you are trying to do?

Thanks,
Kirupa :nerd_face:

Thanks for the suggestion, although click won’t work for this use case. I’m adding paragraphs within a larger text window. Each paragraph is within a div tag, and the entire text window is inside the parent div. Every time the user enters new text, the event listener fires only on the specific div paragraph

Can you provide a link to your page or a smaller repro? This seems like a very reasonable scenario, so there has to be some magical approach that works. I’d love to fiddle with it for a bit :stuck_out_tongue:

1 Like

I am making a calculator pls how can i apply this to mine?Your explanation was legit but i learn slow

2018-05-26_21h22_04