AJAX reconstructs accordion elements and shatters them apart

I've implemented Search & Filter Pro to sort through images on a website. The checkbox filters are contained within accordions. Initially, everything functions correctly when the page loads. However, an issue arises when I click on a filter as it triggers an AJAX request that refreshes the filters and removes my accordion classes.

The Search & Filter plugin includes a couple of hooks, one that activates during the start of AJAX and another upon completion. While I can reapply the accordion script once AJAX is finished, the problem lies in losing track of which accordion was open previously.

Below is the code for the accordion...


ecg.accordions = ecg.accordions || {};

ecg.accordions = {
    scrollToDiv: function(element){
        var offset = element.offset();
        var offsetTop = offset.top - 40;
        $('body,html').animate({
            scrollTop: offsetTop
        }, 500);
    },
    
    init: function(){
        $('li[data-sf-field-type="taxonomy"]').not(':first').children('ul').hide();
        $('li[data-sf-field-type="taxonomy"]:first-child h4').addClass('expanded')

        $('li[data-sf-field-type="taxonomy"] h4').click(function(){

            if ($(this).hasClass('expanded')){
                $(this)
                    .next()
                    .slideUp('fast');
                $(this).removeClass('expanded');
            }else{
                $('.expanded').each(function(){
                    $(this)
                        .next()
                        .slideUp('fast');
                    $(this).removeClass('expanded');
                });                     
                $(this)
                    .next()
                    .slideDown('fast', function(){
                        var el = $(this);
                        ecg.accordions.scrollToDiv(el);
                    });
                $(this).addClass('expanded');
            }
            return false;
        });
    } // init
} // ecg.accordions

Here are the hooks provided by the filter plugin...

//detects the start of an ajax request being made
$(document).on("sf:ajaxstart", ".searchandfilter", function(){
    console.log("ajax start");
});

//detects when the ajax request has finished and the content has been updated
$(document).on("sf:ajaxfinish", ".searchandfilter", function(){
    console.log("ajax complete");
    ecg.accordions.init();
});

In the event above, you can see that I rerun the accordion code once completed. I am considering a method where in the ajax start script, I capture the open accordion and pass it to the completed ajax event, although I'm unsure if this is feasible.

Below is a sample markup for a couple of filters


<div class="filters">
   <form action="#" method="post" class="searchandfilter" data-sf-form-id="20" data-is-rtl="0" data-results-url="#" data-ajax-form-url="#" data-use-history-api="1" data-template-loaded="1" data-lang-code="" data-ajax="1" data-ajax-target=".listing" data-ajax-links-selector=".pagination a" data-update-ajax-url="1" data-only-results-ajax="1" data-scroll-to-pos="0" data-auto-update="1" data-auto-count="1" data-auto-count-refresh-mode="1" id="search-filter-form-20" autocomplete="off">
      <ul>
         <li class="sf-field-taxonomy-ce_venue" data-sf-field-name="_sft_ce_venue" data-sf-field-type="taxonomy" data-sf-field-input-type="checkbox">
            <h4>Venue</h4>
            <ul data-operator="or" class="">
               <li class="sf-level-0 sf-item-39" data-sf-count="2">
                    <input  class="sf-input-checkbox" type="checkbox" value="ven1" name="_sft_ce_venue[]" id="sf-input-60a0def98ed13c6d6f9a27aee1c13e70">
                    <label class="sf-label-checkbox" for="sf-input-60a0def98ed13c6d6f9a27aee1c13e70">ven1<span class="sf-count">(2)</span></label>
                </li>
               <li class="sf-level-0 sf-item-42" data-sf-count="1">
                    <input  class="sf-input-checkbox" type="checkbox" value="ven2" name="_sft_ce_venue[]" id="sf-input-500ece294de752754740cc49b255a686">
                    <label class="sf-label-checkbox" for="sf-input-500ece294de752754740cc49b255a686">ven2<span class="sf-count">(1)</span></label>
                </li>
            </ul>
         </li>
         <li class="sf-field-taxonomy-ce_color" data-sf-field-name="_sft_ce_color" data-sf-field-type="taxonomy" data-sf-field-input-type="checkbox">
            <h4>Color</h4>
            <ul data-operator="or" class="">
               <li class="sf-level-0 sf-item-81" data-sf-count="2">
                    <input  class="sf-input-checkbox" type="checkbox" value="color1" name="_sft_ce_color[]" id="sf-input-39bf68813f58db9c7013a386ac7d5201">
                    <label class="sf-label-checkbox" for="sf-input-39bf68813f58db9c7013a386ac7d5201">color1<span class="sf-count">(2)</span></label>
                </li>
               <li class="sf-level-0 sf-item-43" data-sf-count="1">
                    <input  class="sf-input-checkbox" type="checkbox" value="color2" name="_sft_ce_color[]" id="sf-input-68bb548aeaab5440359a3afbdf9d9513">
                    <label class="sf-label-checkbox" for="sf-input-68bb548aeaab5440359a3afbdf9d9513">color2<span class="sf-count">(1)</span></label>
                </li>
            </ul>
         </li>
      </ul>
   </form>
</div>

Answer №1

One solution that comes to mind is keeping track of which accordion was open so it can be reopened if necessary.

Instead of attaching event handlers directly to individual elements, using event bubbling ensures that the init function is only called once.

For example:

        $('.filters').on('click', 'li[data-sf-field-type="taxonomy"] h4', function(){

When an accordion is activated, we can store a reference to the expanded list item. Using the content of <h4> tags as identifiers seems like a viable option.

In the click handler section where you have:

                $(this).addClass('expanded');

We can add:

                var $h4 = $(this); // ...
                $h4.parents('.filters').data('reference', $h4.text());

(I simplified the code by reusing the queried object ($(this)) instead of generating it each time, e.g.

var $h4 = $(this); $h4.next().slideUp('fast'); //...
)

To implement this for the default accordion as well, modify the init function from:

        $('li[data-sf-field-type="taxonomy"]:first-child h4').addClass('expanded')

To:

        var $first_h4 = $('li[data-sf-field-type="taxonomy"]:first-child h4');
        $first_h4.addClass('expanded').parents('.filter').data('reference', $first_h4.text());

Next, update the search and filter complete code to make use of this feature:

//detect when the ajax request has finished and the content has been updated
$(document).on("sf:ajaxfinish", ".searchandfilter", function(){
    console.log("ajax complete");
    //ecg.accordions.init();
    // Loop through each filter, check if they had an expanded accordion, loop through the new h4's to see if there's match.
    $('.filters').each(function(){
        var $filter = $(this);
        var id     = $filter.data('reference');
        if (id) {
            $filter.find('li[data-sf-field-type="taxonomy"] h4:contains(' + id + ')').toggleClass('expanded', true);
        }
    });
});

If you want a specific accordion to always remain open, similar to how the first item is opened with init, create a separate function for this and reuse it in the search & filter complete event as well. Some adjustments may be needed, but it should work effectively.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Configuring settings for gulp-jshint

I'm currently utilizing gulp to compile my JavaScript code. My intention is to run jshint on the files before concatenating them, but I'm encountering difficulties in configuring the options properly. Am I handling this process correctly? var re ...

Is there a way to place two input fields from different forms side by side on the same line?

Here are two forms extracted from an html page: <form method="get" action="search/s" id="number"> <div style="text-align: center;"> <input type="text" id="regNo" name="regNo" size="30" maxLength="50" > or ...

The function Yii2 isAjax returns a boolean value of false

Encountered an issue while attempting to run some code, even though it worked previously. Using AJAX to execute the code, but it resulted in a 500 error. Came across several posts with similar issues and their solutions, but none of them resolved the iss ...

Quantify the duration it takes for Angular to display a directive

Is there a method to gauge the duration taken by Angular to render a directive? Alternatively, is there a way to determine the time it took Angular to recognize a change in a dataset and display the contents of the updated dataset? As an illustration, co ...

Notes on using touch devices with Angular

My goal is to make my Angular application capable of displaying a footnote on mobile devices when text injected with nativeElement.innerHTML is clicked. I attempted to explain this before but feel that I didn't do it justice, so here's another sh ...

When two divs are activated, the third div goes into invisible mode

I attempted this code, but it doesn't appear to be functioning correctly. if(div1)&& (div2)==display:none { div3=display:none; } else { div3=display:block; } ...

Is it considered fashionable to utilize HTML5 data attributes in conjunction with JavaScript?

Currently, I am utilizing HTML5 data attributes to save information such as the targeted DOM element and to initialize events using jQuery's delegation method. An example of this would be: <a href="#" data-target="#target" data-action="/update"> ...

"Deploying code to Heroku using Node.js: A guide to adding git commits directly on

Currently, as I delve into learning Node and Git, I'm faced with a dilemma involving my Heroku app. The app is designed to interact with a local file on the server that serves as a basic JSON database. The issue arises when I attempt to manage this f ...

Use multiple lines to create a stunning visualization using morris.js. Let your data

I need help creating a graph using morris.js with 2 lines. I have two sets of data in one array, but I can't seem to display both lines on the graph simultaneously. When I use todos[0], only the first line is visible, and when I use todos[1], only the ...

What is the best way to specify the CSS :hover state within a jQuery selector?

I am trying to change the background color of a div element when hovered using jQuery, but for some reason the following code is not working as expected: $(".myclass:hover div").css("background-color","red"); Can someone provide guidance on how to achiev ...

Is it possible for the Jquery Accordion to retract on click?

Hello everyone, I've created an accordion drop-down feature that reveals content when the header of the DIV is clicked. Everything works fine, but I want the drop-down to collapse if the user clicks on the same header. I am new to JQUERY and have trie ...

Using Node.JS and MySQL to optimize data retrieval by combining queries to define variables and select data efficiently

My SQL query that functions perfectly in PHPMyAdmin seems to be causing issues in Node.JS: SET @row_number=0; SELECT * FROM blah blah @row_number blah blah; When attempting to execute both queries in Node.JS using con.query("SET @row_number=0; SELECT * ...

Activate the following and preceding elements within the 'vue-owl-carousel' component for Vue.js

I'm facing an issue with navigating to the next or previous item in the vue-owl-carousel package while using Vue and javascript. Is there anyone who can provide assistance? Below is the code for the carousel : <carousel id="newGamesCarousel" :it ...

How can you use CSS animations to animate two images in a way that hides one while showing the other?

click here for the image link visit this webpage for the link I need I am looking to add an animated section to my website. The inspiration comes from the webpage linked above, where images slide down one after another in a seamless manner. I attempted t ...

How to use jQuery with multiple selectors?

I am attempting to create a feature where multiple links are highlighted when one is clicked. However, I am encountering an issue where only the link that is clicked is being highlighted, rather than all three links. Below is the code snippet: $('#v ...

How come I am unable to terminate this Angular HTTP request?

I've been trying to implement a solution for canceling HTTP requests in AngularJS by referring to this Stack Overflow post. Here's the code snippet I'm using: var canceller = $q.defer(); $timeout(function() { canceller.resolve(); alert ...

How to Determine If a String Represents an HTML Tag Using jQuery

Checking if a string is an HTML tag can be tricky. I've tried various methods found on Google, but none have been successful so far: var v = $(string).html() ? 1 : 0; --or---------------------------------------------- var htmlExpr = new RegExp("/^( ...

In Node.js, the mongodb+srv URI does not support including a port number

Currently, I am working on a project in nodejs using express js. I have encountered the following error: MongoDB connection error: MongoParseError: MongoDB+srv URI cannot contain a port number. In my "MongoDB Atlas" dashboard, I have obtained my "connecti ...

Add data to a nested array with Vuex

Currently, I am facing a challenge with adding an object to a nested array in Vue using store / vuex. While I have successfully updated the main comments array with a mutation, I am struggling to identify the specific main comment to which the new reply ob ...

Error message displayed: "An error occurred while processing the VueJS InertiaJS Uncaught (in promise) TypeError. It seems that the property 'search'

Currently, I am working on a Vue JS project with Inertia. One of the features I am implementing is a list that allows users to filter by name. data() { return { selectedUser: this.value, selected: null, search: & ...