0

I have being trying to solve a problem of how to bind a clicked element to functions using JavaScript, and after many days finally found a solution here provided by @JerdineSabio. My original problem was asked at Stackoverflow and it had to do with a single JavaScript script handling many buttons and performing speech recognition. The code that binds the clicked button to the functions is written in jQuery and involves using function(e) . The jQuery statement takes 3 parameters, but in JS the equivalent expression only takes two (the event (eg. the clicking) and the function). I've looked up the usual references I depend upon but haven't found a way to write it in Javascript. I have solved the scripting problem; so I just want to find an answer to this question in case I might want to use JS only in the future, as I tend to rely on JS more than jQuery, and I also read about function(e) before and watched a video on Youtube about it, but I still did not quite understand what "e" is and what it does.
The jQuery script below works as it should. The code changes the color of the button that's next to it. Once again, this involves multiple buttons but there is only one script for them all.

I have tried:

document.addEventListener("click", myFunction); 
function myFunction(e) {
this.previousElementSibling.setAttribute("style", "background-color: gold") ...
....};

and I've tried a few more things, but I can't make the function work correctly no matter what I try.

The HTML is:

 <div class="container">
  <button id="first" type="button" class="btn btn-danger"></button>
  <button id="first" type="button" class="btn btn-warning"></button>
  </div>

The jQuery is:

 $(document).ready(function(){
     $(document).on("click", "#first", function(e) {
        $(this).siblings().css("background-color", "gold");
        });
  }); 
Honeybear65
  • 311
  • 1
  • 10
  • 1
    You have two `id="first"`. Unique IDentifiers are supposed to be unique. Use classes instead – Jeremy Thille Jan 18 '21 at 13:19
  • You need to use [document.querySelectorAll('#first')](https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelectorAll) to find the elements to add the event listener to. – Rup Jan 18 '21 at 13:20
  • I purposely made the id and class the same for the two buttons as I am testing how to bind the clicked element to the function, without using the ```document.getElementById``` expression. This problem was part of a larger script involving many functions and dealing with many buttons that could be clicked. I had to find a way for the clicked element being identified and used in the other functions. Otherwise I had to write a separate JS script for each button, which I didn't want to do. – Honeybear65 Jan 18 '21 at 13:22
  • The jquery is using event delegation - it binds one function to the document and then when document click happens it checks if the thing that was clicked matches the selector. That part is covered [here](https://stackoverflow.com/questions/25248286/native-js-equivalent-to-jquery-delegation) – James Jan 18 '21 at 13:24
  • 1
    Here's example code: https://jsfiddle.net/9y42nrwf/ –  Jan 18 '21 at 13:29
  • @ChrisG I don't know how to mark your answer as solving the question; the script works perfectly and it was what I was looking for. Thank you. – Honeybear65 Jan 18 '21 at 13:44

3 Answers3

1

You can do in many ways by adding onclick function to the button then target them by id or class name

First by the id (ofc must have unique id) then you gonna put :-

        function btnClicked(clicked_btn) {
            let btn = document.getElementById(clicked_btn)
            btn.style.backgroundColor = 'red'
        }
    <div class="container">
        <button id="first" onClick='btnClicked(this.id)' type="button" class="vtn btn-danger">press1</button>
        <button id="second" onClick='btnClicked(this.id)' type="button" class="btn btn-warning">press2</button>
    </div>

second by class name :- but you have more than class name so we gonna add split() function to target one of them like so

        function btnClicked(clicked_btn) {
            let btn = document.querySelector('.' + (clicked_btn.split(' ')[1]))
            btn.style.backgroundColor = 'red'
        }
    <div class="container">
        <button id="first" onClick='btnClicked(this.className)' type="button" class="btn btn-danger">press1</button>
        <button id="second" onClick='btnClicked(this.className)' type="button" class="btn btn-warning">press2</button>
    </div>
  • What if you don't want to put the "onClick" on the button? I didn't want to do that, because I would have to wrap the whole script in that function and I didn't want to do that. I tried that first in my Speech Recognition script and it created some problems. So I tried to make a script without doing that and it worked but only with jQuery code. @MohamedGhoneim – Honeybear65 Jan 19 '21 at 03:52
  • This is the speech recognition script where the original problem was posed: [here](https://stackoverflow.com/questions/65760301/how-can-i-bind-an-element-thats-clicked-to-a-function-in-a-script-for-speech-re?noredirect=1&lq=1). I found the answer from @JerdineSabio [here](https://stackoverflow.com/questions/60786191/javascript-make-event-click-and-speech-recognition-work-with-multiple-buttons). That's where I came across the jQuery code that solved my problem. I'm wondering what the equivalent JS code is that will produce a `$(this)` . But your script works as is. @MohamedGhoneim – Honeybear65 Jan 19 '21 at 04:02
0

You can try this way:

<script>
    window.addEventListener('load', () => {
        var buttons= document.querySelectorAll('#first');
        for (var i = 0; i < buttons.length; i++)
            buttons[i].addEventListener("click", myFunction);
    });

    function myFunction(e) {
        siblings(e.target).forEach((element, index) => {
            element.setAttribute("style", "background-color: gold")
        });
    };

    function siblings(elem) {
        let siblings = [];
        if (!elem.parentNode)
            return siblings;
        let sibling = elem.parentNode.firstElementChild;
        do {
            if (sibling != elem)
                siblings.push(sibling);
        } while (sibling = sibling.nextElementSibling);
        return siblings;
    };

</script>
Vahid
  • 655
  • 4
  • 11
0

Basic event delegation requires you to have to figure out what was clicked is the element you are looking for. Using matches() makes that easy.

function delegatedClick(selector, callback) {
  function fnc(event) {
    if (event.target.matches(selector)) {
      callback.call(event.target, event);
    }
  }
  document.addEventListener("click", fnc);
}

delegatedClick(".foo button", function() {
  console.log(this.id);
});
.foo button {
  color: green;
}
<div class="foo">
  <button type="button" id="b1">Yes</button>
</div>
<div class="foo">
  <button type="button" id="b2">Yes</button>
</div>
<div class="bar">
  <button type="button" id="b3">No</button>
</div>
<div class="foo">
  <button type="button" id="b4">Yes</button>
</div>

Now toggling the siblings

var wrapper = document.querySelector(".foo");
var buttons = wrapper.querySelectorAll("button")
wrapper.addEventListener("click", function (event) {
  var clickedButton = event.target.closest("button");
  clickedButton.classList.remove("goldbg");
  buttons.forEach(function (btn) {
    if (btn !== clickedButton) {
       btn.classList.add("goldbg"); 
    }
  });
});
.foo button.goldbg {
  background-color: gold;
}
<div class="foo">
  <button type="button" id="b1">1</button>
  <button type="button" id="b2">2</button>
  <button type="button" id="b3">3</button>
  <button type="button" id="b4">4</button>
</div>
epascarello
  • 204,599
  • 20
  • 195
  • 236