2

Could some one please tell me how to refactor the JavaScript without "this" in a way that explains the use of "this" in the browser context? (please don't answer the following question with jQuery solutions)

I have passed e (event) into the callback function and implemented:
e.target.classList.toggle("active");
var panel = e.target.nextElementSibling;

What I have implemented has worked. However I want to know to what benefit/why you would use "this" in the context shown, it doesn't seem as declarative as e.target. Is this a function binding issue?

(other snippet related questions below snippet)

I have taken the below snippet from W3schools, it is used for creating an accordion menu:

var acc = document.getElementsByClassName("accordion");
var i;

for (i = 0; i < acc.length; i++) {
  acc[i].addEventListener("click", function() {
    /* Toggle between adding and removing the "active" class,
    to highlight the button that controls the panel */
    this.classList.toggle("active");

    /* Toggle between hiding and showing the active panel */
    var panel = this.nextElementSibling;
    if (panel.style.display === "block") {
      panel.style.display = "none";
    } else {
      panel.style.display = "block";
    }
  });
}
/* Style the buttons that are used to open and close the accordion panel */
.accordion {
  background-color: #eee;
  color: #444;
  cursor: pointer;
  padding: 18px;
  width: 100%;
  text-align: left;
  border: none;
  outline: none;
  transition: 0.4s;
}

/* Add a background color to the button if it is clicked on (add the .active class with JS), and when you move the mouse over it (hover) */
.active, .accordion:hover {
  background-color: #ccc;
}

/* Style the accordion panel. Note: hidden by default */
.panel {
  padding: 0 18px;
  background-color: white;
  display: none;
  overflow: hidden;
}
<button class="accordion">Section 1</button>
<div class="panel">
  <p>Lorem ipsum...</p>
</div>

<button class="accordion">Section 2</button>
<div class="panel">
  <p>Lorem ipsum...</p>
</div>

<button class="accordion">Section 3</button>
<div class="panel">
  <p>Lorem ipsum...</p>
</div>

Could some one please explain the relationship between the loop and the event listener?

The way I understand it, I would expect that:
every time the DOM is altered/reloads =>
the loop runs =>
adding an event listener on each html collection element=>
if HTML element clicked == true (or) the event is fired
the class is toggled to active
the following if statement is executed and styles may or may not be applied inline to the html element. This seems like a computationally expensive method of adding event listeners.

Is the above statement that I have written correct if not exactly what is happening?
Is there a more efficient way of writing the event listener loop snippet?
I.e. using event bubbling on a containing element perhaps?

Byron
  • 75
  • 9
  • "_Is there a more efficient way of writing the event listener loop_" Yes, [there is](https://stackoverflow.com/q/1687296/1169519), if you're interested in performance, see also https://stackoverflow.com/a/66480319/1169519 . You can also move the panel visibility changes from JS to CSS, use [Adjacent sibling combinator](https://developer.mozilla.org/en-US/docs/Web/CSS/Adjacent_sibling_combinator). – Teemu Feb 23 '22 at 06:51
  • [`this` in an anonymous function in the callback is equivalent to `e.currentTarget`](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#the_value_of_this_within_the_handler) (the element that the listener is bound to). This is [distinct from `e.target`](https://stackoverflow.com/questions/10086427/what-is-the-exact-difference-between-currenttarget-property-and-target-property). – Terry Feb 23 '22 at 06:54
  • Also, "_every time the DOM is altered/reloads => the loop runs_" No, the loop is run only once when the page loads. If you're adding/removing elements, the event delegation makes thing easier. Check [an example](https://jsfiddle.net/vj0dgpyo/) of how you can delegate the event in your case. – Teemu Feb 23 '22 at 07:06
  • Hey @Teemu thanks for your input, I will definitely give all of the resources you supplied a thorough review. – Byron Feb 23 '22 at 09:01
  • Hey @Terry Thanks for your comment I will definitely give this a good read. – Byron Feb 23 '22 at 09:02

1 Answers1

1

Here's a simple HTML layout:

<main>
  <div class='A'></div>
  <section></section>
  <div class='B'></div>
  <section></section>
  <div class='C'></div>
</main>

Here's the JavaScript using a programming paradigm called Event Delegation:

document.querySelector('main').addEventListener('click', eventHandler);

Because the majority of the events bubble (click bubbles), the event listener should be registered to an ancestor tag (in this example that would be <main>) of the tags you want to control via events (in this example it's .A, .B, and .C). Now the event handler:

function eventHandler(event) {
  const listener = event.currentTarget; // or `this` points to `<main>`
  const clicked = event.target; // This is the tag user actually clicked
    ....

We need to control exactly what reacts to a click and what doesn't react when clicked. We can use if/if else or switch() or even a ternary to delegate events to what we want while excluding what we don't want. Continuing within eventHandler(e)...

....
  // All <section>s and even <main> is excluded
  if (clicked.matches('div')) {
    
    if (clicked.matches('.A')) {
       clicked.style.background = 'red';
    }
    if (clicked.matches('.C') {
       clicked.style.background = 'blue';
    }
  }
  // .B was never mentioned with any specific intructions so it's also excluded.
 }

The example below is pretty much the same delegation scheme as the previous explination except with an additional CSS trick with adjacent sibling combinator:

.accordion.active+.panel {
  display: block;
}

Whenever a button is .active, the .panel that follows it will disappear.

document.body.addEventListener("click", togglePanel);

function togglePanel(e) {
  const clk = e.target;

  if (clk.matches('.accordion')) {
    clk.classList.toggle("active");
  }
};
.accordion {
  background-color: #eee;
  color: #444;
  cursor: pointer;
  padding: 18px;
  width: 100%;
  text-align: left;
  border: none;
  outline: none;
  transition: 0.4s;
}

.active,
.accordion:hover {
  background-color: #ccc;
}

.panel {
  padding: 0 18px;
  background-color: white;
  display: none;
  overflow: hidden;
}

.accordion.active+.panel {
  display: block;
}
<button class="accordion">Section 1</button>
<div class="panel">
  <p>Lorem ipsum...</p>
</div>

<button class="accordion">Section 2</button>
<div class="panel">
  <p>Lorem ipsum...</p>
</div>

<button class="accordion">Section 3</button>
<div class="panel">
  <p>Lorem ipsum...</p>
</div>
zer00ne
  • 41,936
  • 6
  • 41
  • 68
  • thanks this is a beautiful answer, thank you for putting this together. I had an inkling that something like this maybe more elegant but didn't know where to start. Could I just ask, in your opinion why do you think w3schools would supply this as correct way of doing things? When this is a site that purports to help people learn! Do you think this is just a simple modular and therefore easy way for them to show implementation, disregarding the potential caveats of course? – Byron Feb 23 '22 at 08:58
  • IMO, w3schools is ok for beginners, but as for a reference for more advanced concepts it fails to do so. If not forwarned, a novice may rely too heavily on such an easily searchable resource and develop *"bad habits"*. [MDN](https://developer.mozilla.org/en-US/) is a complete resource and I also find [Dottoro](http://help.dottoro.com/) to be an accurate resource. Here's a discussion on [w3fools](https://www.reddit.com/r/javascript/comments/19kgua/is_w3fools_really_a_useful_resource/), an objector of w3shools. – zer00ne Feb 23 '22 at 20:55
  • Thanks for that I do try to stick to MDN never heard of Dottoro though, so thank you for that resource, it looks good. Interesting about w3fools, I guess you would hope w3 would know better. Thanks again for your help. – Byron Feb 24 '22 at 10:15