How to convert this jQuery snippet to vanilla JS

How to convert this jQuery snippet to vanilla JS
0.0 0

#1

Hi,

While I’m learning JavaScript, I started to work on the following example

https://codepen.io/itsthomas/pen/wxwRYx?editors=1010

The script sends an Ajax call and gets back a simple JSON file.
I fetch the response and build a simple unordered list using Handlebars.js.
So far so good.

Now I wanted that if I click on the button in front of each character, a hidden div becomes visible, which contains some details about the character.
I achieved this easily using jQuery.
However, I spent a whole day to convert that small jQuery snippet to vanilla JS and failed badly. :frowning:

How can I change the jQuery snippet, which shows and hides the div to vanilla JS?
It’s not important if it slides or not. It would be enough if I could just show and hide it by adding the class visible to it.

This is the jQuery code, I want to convert to vanilla JS:

// jQuery solution using event delegation for slideToggle
$('#container').on('click', 'button.btn', function() {
  console.log($(this)); // this is the button with the class btn

  // If the button is clicked, add a class to .details
  $(this).parent().parent().next($('.details')).slideToggle();
})

#2

Is there any reason you can’t simply use a document.querySelector or document.getElementById and modify its classList via classList.add('visible')?

I’m a bit green when it comes to front end web dev I’m afraid but that’s what I first though of


#3

First, remove the following from your 2nd then method’s callback function:

  const detailsContainer = document.querySelector('.details');
  const listContainer =  document.querySelector('.list-container');
  
  listContainer.addEventListener('click', ({ target }) => {
    if (target.matches('.btn')) detailsContainer.classList.toggle('visible');
  });

Second, move the following line of your createHtml function into the global scope:

let ourContainer = document.querySelector("#container");

Finally, add the following click handler in the global scope:

ourContainer.addEventListener('click', ({target}) => {
  if (target && target.classList.contains('btn')) {
    const detailsContainer = target.parentNode.parentNode.nextElementSibling;
    detailsContainer.classList.toggle('visible');
  }                                                      
});

#4

Is there any reason you can’t simply use a document.querySelector or document.getElementById and modify its classList via classList.add(‘visible’)?

It won’t work. The problem is that all those elements such as .btn and .details are getting generated dynamically. I think when the JavaScript compiler runs, those elements don’t exist and therefor, we can’t access them
So, we need to use event delegation, which means that we have to add the eventLitener to the parent container, which already exists.


#5

I mean that you’d find them at runtime with a selector and then add the class via the class list, not entirely sure how that wouldn’t work but you’ve got a great solution already there


#6

@randelldawson; thanks a lot for the great solution.


#7

Hi,
I know that a syntax like ({target}) is used when an Arrow function is returning an object.
But in this case, why should I use this syntax?
Could you please explain a bit more about ({target}) ?

Thanks


#8

In this case it’s being used to destructure the argument to the arrow function

It finds the member called target of its argument and lets you refer directly to that instead of via its enclosing object