Search code examples
javascripthtmltabletop.js

Toggle hide/show not working on childs div


I have a script that gets data from a Google Sheet and displays it as a webpage - using JS and Tabletop.js.

There are multiple entries in the Sheet thus multiple entries in the webpage. To organise the Data I have a hide/show button. When the button is clicked on the first entry it works. However when the any of the other buttons are clicked it hides or shows the first entries data, not its own!

How do I hide/show each individual entries data? Below is the code I am working with!

I am new to JavaScript - Thanks in advance!

P.S - I struggled writing the Title to the questions!

    <link href="../common/cats-copy.css" media="screen" rel="stylesheet" type="text/css" />

  </head>

    <style>
    #add-info {
    display: none
    }
    </style>

  <body>

      <div class="container">
    <h1>Resturants</h1>

    <div id="content"></div>

    <script id="cat-template" type="text/x-handlebars-template">

      <div class="entry">
        <h5>{{establishment_name}}</h5>
        <h6>Area: {{area}}</h6>
        <h6>Cuisine: {{cuisine}}</h6>
          <button id="btn" class="button-primary" onclick="myFunction()">Hide</button>
        <div id="add-info">
        <h6>Address: {{address}}</h6>
        <h6>Google Maps: {{google_maps_location}}</h6>
        <h6>Opening Times: {{opening_times}}</h6>
        <h6>Rating: {{rating}}</h6>
        <h6>Added By: {{added_by}}</h6>
        <h6>Date Added: {{date_added}}</h6>
        </div>
      </div>

    </script>

      </div>

    <!-- Don't need jQuery for Tabletop, but using it for this example -->
    <script type="text/javascript" src="handlebars.js"></script>
    <script type="text/javascript" src="../../src/tabletop.js"></script>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>

      <script type="text/javascript">
      var public_spreadsheet_url = 'https://docs.google.com/spreadsheets/d/1h5zYzEcBIA5zUDc9j4BTs8AcJj-21-ykzq6238CnkWc/edit?usp=sharing';

      $(document).ready( function() {
        Tabletop.init( { key: public_spreadsheet_url,
                         callback: showInfo,
                         parseNumbers: true } );
      });

      function showInfo(data, tabletop) {
        var source   = $("#cat-template").html();
        var template = Handlebars.compile(source);

        $.each( tabletop.sheets("food").all(), function(i, food) {
          var html = template(food);
          $("#content").append(html);
        });
      }

    </script>

      <script>

                function myFunction() {
    var x = document.getElementById("add-info");
    if (x.style.display === "none") {
        x.style.display = "block";
    } else {
        x.style.display = "none";
    }
}
      </script>

  </body>
</html>

Solution

  • Are all the entries on your page filled from the given template, meaning they are divs with the class entry? If so, I think your issue is the following: Your entry div has a child div with the id="add-info". And when you click the button, your handler function (myFunction()) tries to get a reference to that div via document.getElementById("add-info"); Now, if you have multiple such entries on a page, you will have multiple divs with id="add-info". But the id attribute of an element must be unique in your whole document. See the description of id or that of getElementById().

    So the root cause of your problem is that the same id is used multiple times in the document when it shouldn't be. You get the behavior you're seeing because getElementById() just happens to be returning a reference to the first element it finds on the page, regardless of which button you click. But I believe you're in undefined behavior territory at that point.

    One way to solve the problem is to somehow give myFunction() information about which button was clicked, while making each div you'd like to manipulate unique so they can be found easier. For instance, you can use the order of the restaurant on your page as its "index", and use that as the id of the div you'd like to hide/show. And you can also pass this index as an argument when you call your click handler:

    ...
    <button id="btn" class="button-primary" onclick="myFunction('{{index}}')">Hide</button>
    <div id="{{index}}">
    <!-- The rest of the code here... -->
    ...
    

    ... add the index into your template context, so Handlebars can fill in the {{index}} placeholder:

    ...
    $.each( tabletop.sheets("food").all(), function(i, food) {
      food.index = i    // Give your context its 'index'
      var html = template(food);
      $("#content").append(html);
    });
    ...
    

    ... and then alter your function slightly to use the given argument instead of always looking for the div with id="add-info":

    function myFunction(indexToToggle) {
      var x = document.getElementById(indexToToggle);
      // rest of the code is same
    

    With this approach, I expect your DOM to end up with divs that have ids that are just numbers ("3", "4", etc.) and your click handler should get called with those as arguments as well.

    Also note that your <button> element has id="btn". If you repeat that template on your page, you will have multiple <button>s with the same id. If you start trying to get references to your buttons via id you will have similar issues with them too since the ids won't be unique.