Tuesday, October 4, 2016

what could cause html and script to behave different across iterations of a for loop?

Leave a Comment

I'm trying to build a side navigation bar where categories are listed and upon clicking a category a respective sub list of subcategories is shown right below the category. And if the category is clicked again, the sub list contracts.

So I'm running a loop across category objects. Inside this outer loop, I'm including a an inner loop to list subcategories and a script that hides the submenu and slidetoggles it only when a category is clicked. I'm using django template tags to dynamically assign class names for my html elements and also to refer to them in the script. So after all for loop iterations, there is a list of subcategory and a dedicated script for each category and they have unique class names so no chance of an overlap. So the weird part is, this works perfectly for most categories, but some of the categories and their submenu remain open and when clicked on the category the page reloads.

I don't get it, what could cause the exact same code (run in a for loop) to behave so differently?

This is my code:

{% load staticfiles %} {% load i18n pybb_tags forumindexlistbycat %} {% catindexlist as catindexlisted %}  {% block body %}<div class="col-sm-12 col-md-12 col-xs-12 col-lg-12 body-container leftsidenavigator" style="margin-top:15px;">     <div class="col-sm-12 col-md-12 col-xs-12 col-lg-12 leftsidenavigator-inner" style="padding:0px;">          <h2><center>Categories</center></h2>              <ul class="catindexlist catlistcat nav-collapse89">                    {% for category in catindexlisted %}                          <li class="catindexlistitem category-name{{category.name}}{{category.name}}" style="font-weight:600;padding-right:20px;"><a href="">{{category.name}}</a></li>                          <ul style="padding:0px;" class="nav-collapse88">                          {% for forum in category|forumindexlistbycat %}                                <li class="catlistforum{{category.name}}{{category.name}} forum-name" style="padding-right:10px;"><a href="{{ forum.get_absolute_url }}">{{forum.name}}</a></li>                          {% endfor %}</ul><script>                           $(function() {                               $(".catlistforum{{category.name}}{{category.name}}").hide();                                     $(".category-name{{category.name}}{{category.name}} a").click(function(e) {                                      e.preventDefault();                                      $(".catlistforum{{category.name}}{{category.name}}").slideToggle();                                           if(!($(this).parent('li').siblings('div').children('ul').children('div').is(":visible"))){                                               $(this).parent('li').siblings('div').children('ul').children('div').is(":visible").slideToggle();                                           }});                                       })                                 </script>                            {% endfor %}                            </ul>                       </div>                       </div>                   {% endblock %}      {% block theme_script %}<script src="{% static "pinax/js/theme.js" %}"></script>{% endblock %} 

4 Answers

Answers 1

Try writing the javascript function after the outer for loop, sometimes this might overlap and redirect. And also try providing spaces between the names of category inside "li".

Answers 2

My advice would be to clean up your JS by making a single function to handle all the clicks. You are already using class on clicks, so why not have one function handle all the clicks?

<script>   $(function() {     // Hide all elements with a class starting with catlistforum     $('[class^="catlistforum"]').hide();      // Assign clicks to all links whose parent has a class starting with catlistforum     // (Though why are you hiding the parents? How will they click the links?)     $('[class^="catlistforum"] a').on("click", function(e) {       e.preventDefault();        // Delegate slideToggle to each click target       $(e.target).slideToggle();        // This seems redundant, but hopefully it will behave the way you want it to behave       if(!($(e.target).parent('li').siblings('div').children('ul').children('div').is(":visible"))) {         $(e.target).parent('li').siblings('div').children('ul').children('div').is(":visible").slideToggle();       }     });   }) </script> 

Of course, if I were you, I would just define two new classes (e.g. catlistforum-hide and catlistforum-toggle) which I would apply to all the elements I wanted to hide and toggle, respectively.

Answers 3

Is there any chance that the category name contains spaces?

Just a tip: You are not using good practice in your code. IMO you should get your javascript code outside of the forloop and remove {{ category_name }} classes. catindexlistitem on click should toggle hidden class (i noticed you use bootstrap) to it's child ul.

By adding a more generic event listener you simplify your code and by using css you improve performance. In case you want to add effects you still can with css3.

Answers 4

The most probable cause is the use of {{category.name}} for class names.

The code snippet doesn't show what values is accepted for category.name and my guess is it can be user input? See naming rules in section Attribute Values what is valid for class names.

It can be solved using template tag slugify ({{category.name|slugify}}) but my recommendation is to try re-design the solution a bit.

If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment