Reputation: 1345
I am using Search & Filter Pro to filter images on a site. I have the checkbox filters inside accordions. On page load everything works right. The problem is when I click a filter it uses AJAX which recreates the filters and wipes out my accordion classes.
The Search & Filter comes with a couple hooks, one that runs when the AJAX starts and one when completed. I can rerun the accordion script when AJAX is complete but I lose which accordion is open.
Here is the accordion code...
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 the filter plugin offers...
//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();
});
You can see in the completed event above I rerun the accordion code. I was thinking maybe in the ajax start script I get the open accordion and pass it to the completed ajax event, I don't know if thats possible.
Here is a sample markup for a couple 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>
Upvotes: 2
Views: 403
Reputation: 3655
Well the idea that comes to mind for me is keeping a note on which accordion was open so we can reopen it, if applicable.
Instead of applying the event handlers directly to the individual elements themselves, rely on event bubbling so we only call init
once.
So:
$('li[data-sf-field-type="taxonomy"] h4').click(function(){
Becomes:
$('.filters').on('click', 'li[data-sf-field-type="taxonomy"] h4', function(){
When an accordion is activated, we can "save" the reference to the expanded list item. To me it seems the content of <h4>
's can be used to identify them, so we'll use that as an example.
So in the click handler where you have:
$(this).addClass('expanded');
We can add:
var $h4 = $(this); // ...
$h4.parents('.filters').data('reference', $h4.text());
(I cleaned up the code so instead of having to generate a query object ($(this)
) each time, reuse one, e.g. var $h4 = $(this); $h4.next().slideUp('fast'); //...
)
Update the default accordion to do the same, so where init
has:
$('li[data-sf-field-type="taxonomy"]:first-child h4').addClass('expanded')
We turn it into:
var $first_h4 = $('li[data-sf-field-type="taxonomy"]:first-child h4');
$first_h4.addClass('expanded').parents('.filter').data('reference', $first_h4.text());
Then update the search and filter complete code to utilize this:
//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();
// 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 something to always be open like you open the first item via init
, put that code in its own function and reuse it in the search & filter complete event, too. Would need some tweaking, but yeah.
Upvotes: 1