javascriptjqueryonclick

How can I remove one of the clicks from hide/show?


I'm trying to show/hide more info about names on click, but don't know how to make it work of a single click.

I'm fetching JSON that has game related info and creating a div with JS like so:

fetch("thething.json")
.then(res => res.json())
.then(data =>{
games = data.map(game => {
const newDiv = document.createElement("div");
newDiv.className = "game-info";
newDiv.innerHTML = `
<p onclick="toggler()";>${game.Name}</p>
<div class="info">
<img src="${game.Thumbnail}">
<p>${game.Description}</p>
</div>
`;
document.body.appendChild(newDiv);
return {name: game.Name, element: newDiv}

JSON itself has a bunch of info on games in structure like this:

[{"Name: "gamename1",
  "Thumbnail: "https://imglocation",
  "Description": "This game is indeed a game"
 },
  {"Name: "gamename2",
  "Thumbnail: "https://imglocation2",
  "Description": "This game2 is indeed a game2"
 }
]

The toggler function is written like this:

function toggler(){
    $('div.game-info').click(function(e){
        $(this).children('.info').toggle();
    });
}

It kinda works, but it takes one or more clicks to view the additional info. I know the problem is due to multiple onclick calls, but don't know how to make it with a single click. I tried using jQuery without the toggler function, but then it opens info on all the names and not just the one which was clicked. So if someone could either tell me how to get rid of that secondary onclick or how to properly target the info which I clicked in the innerHTML section that'd be great!

Answer found. Quick follow-up question. I'm, using this set-up to look up games, but it wont work with the new code, so is there a way to use

return { name: game.Name, element: newDiv }

from the previous version in jQuery?

searchInput.addEventListener("input", e=>{
    const value = e.target.value.toLowerCase()
    games.forEach(game =>{
        const isVisible = game.Name.toLowerCase().includes(value)
        game.element.classList.toggle("hide", !isVisible)
    });
})

Quick search found the answer if someone stumbles across this in the future. seachInput grabs input="search", then hides things that don't match:

const searchInput = document.querySelector("[data-search]")
$(searchInput).on("keyup", function(){
    let value = $(this).val().toLowerCase();
    $(".toggle-info").filter(function(){
        $(this).toggle($(this).text().toLowerCase().indexOf(value) > 
-1)
        });
});

Solution

  • You're toggle() function is what is establishing the click event. This is why you have to click once before anything happens, there is no event listener before that. Also every time you click it, it will add a new event handler, which is why you're getting odd repeated behaviors. This is one reason why it's consider bad practice to put javascript in the html these days, keep the javascript between the <script> tags.

    Also $('div.game-info') is selecting all divs with a class of game-info, which is probably why it's toggling everything. You probably want something like $('div').on('click', '.game-info') The .on() method allows you to provide a second selector argument, which then in the handler function this refers to the specific element that triggered the event, rather than all of them.

    If your project is using jQuery, might as well use it! Here's what that might look like:

    $.getJSON('thething.json')
      .done(function(data) {
        $('body').append(data.map((game) => `
          <div class="game-info">
            <p class="toggle-info">${game.Name}</p>
            <div class="info">
              <img src="${game.Thumbnail}" alt="${game.Name} thumbnail">
              <p>${game.Description}</p>
            </div>
          </div>`).join(``))
        .find('.info').hide();
      })
      .fail(function(error) {
        //handle errors here
      });
    
    $('body')
      .on('click', '.toggle-info', function() {
        $(this).siblings('.info').slideToggle();
      });
    

    $.getJSON() - assuming your data source returns valid JSON, the first argument of the .done() method will be a parsed JSON object, no extra steps. And if the source isn't valid, you can handle that in the .fail() method.

    Next, rather than mutating the DOM for every single game, you could create one giant html string and only have to mutate the DOM once. Then, again, you can put the event handler on some parent of the dynamically added html (body in this case), and use the selector argument to select a specific element that should handle the event.