I have a HTML5 application which is using jquery 3.2.1.
In part of the application - a search feature - I make an ajax request. The response from the ajax request is HTML, and this includes a <script>
tag which links to a js file which is hosted on the same server as the application.
So the ajax code looks like this - for making the ajax request and writing the response to a div with the ID #ajaxContent
:
$.ajax({
url: $('#searchRegulations').attr('action'),
type: 'post',
cache: false,
data: $('#searchRegulations').serialize()
}).done(function (response, status, xhr) {
if (response) {
$('main .content').hide();
$('#ajaxContent').html(response).show();
return false;
}
}
});
If I inspect #ajaxContent
I can see that the <script>
tag is included in the ajax response:
I have also checked my Network tab to make sure /js/search_regulations.js
is being loaded correctly, and it's giving a 200 response:
Inside search_regulations.js
there is some jquery which toggles some filters that are present in #ajaxContent
.
The problem is that this code only seems to be working about 50% of the time. When it works it will toggle the state of some filter buttons by adding/removing a class
.active
to elements inside.browse-ctp__filters-data
and then writing them to a hidden form with the ID#tmpFilters
.
To ensure the script was "firing" I put in the line console.log('search_regulations.js firing');
and sure enough this is shown in the Console every time irrespective of whether the script functions or not.
What's stranger is that if I cut/paste the code into my Console after the ajax response has been written to the page, it always works as expected.
Is this some issue with the way the script is being brought into the page?
I've pasted the script below, but I don't think it's an issue with the code in it, rather the way the browser/ajax response is being handled:
$(function() {
console.log('search_regulations.js firing');
/* toggle the active (applied) state on browse by filters */
/* @link https://stackoverflow.com/questions/48662677/switch-active-class-between-groups-of-include-exclude-buttons */
$(document).on('click', '.browse-ctp__filters-data .include, .exclude', function(){
var $this = $(this);
// Split name into array (e.g. "find_355" == ["find", "355"])
var arr = $this.attr('name').split('_');
// Toggle active class
$this.toggleClass("active");
if ($this.siblings().hasClass("active")) {
$this.siblings().removeClass("active")
}
// Remove any existing instances of the filter from hidden form
$('#tmpFilters input[value="exclude_'+arr[1]+'"]').remove();
$('#tmpFilters input[value="find_'+arr[1]+'"]').remove();
// If a filter has been applied then add it to hidden form
if ($this.hasClass('active')) {
$('#tmpFilters').append('<input type="hidden" name="tmpFilter[]" value="'+$this.attr('name')+'">');
}
});
});
I've offered a bounty because it's not a trivial problem to solve - demonstrated by the fact nobody has given a workable answer. I expect the correct answer to:
setTimeout
or whatever). If the user interacts with the UI elements - such as buttons - returned in the HTML response before the timeout and therefore js is fired... that just leads to the same problem we have now. So that isn't a valid solution as far as I'm concerned.Several people have asked for a fiddle or demo. No 2 people would get the same outcome - which is very much the point of the question - so I didn't make one originally. The HTML returned that gets written to #ajaxContent
is shown here: http://jsfiddle.net/v4t9j32g/1/ - this is what dev tools in the browser shows following the ajax response. Please note that the length of the content returned can vary, since it's the response to a keyword search facility that brings back a load of filter buttons. Also note that this HTML response contains the line <script src="/js/search_regulations.js"></script>
. That is where the problematic js is located and the full contents of that are shown above in this question - the bit that includes console.log('search_regulations.js firing')
ajax returns immediately and the next statement, return result; , is executed before the function you passed as success callback was even called.
What you need to do is pass a callback function to the somefunction as a parameter. This function will be called when the process is done working (ie, onComplete): somefunction: function(callback){ var result = ""; myAjax = new Ajax.
ajax returns, which is a jqXHR object that conforms to the promise interface. If there is a failure, the outer fail function is invoked. The outer fail function is also invoked if the processData function fails. When both the getData and processData functions are successful, the outer done method is invoked.
In the jQuery ajax() function we are not providing any content type or data type. So by default it will return JSON data as expected.
One of the problems that I see is that you're binding the onclick
to the same elements multiple times...
Since the js can be loaded multiply times via ajax requests, it is important to first detach, before attaching again events.
The other problem, is that you're running the code in $(document).ready
event (that is when HTML-Document is loaded and DOM is ready), however you'd probably be better off to run the code in the $(window).load
event (which executes a bit latter, when complete page is fully loaded, including all frames, objects and images)
Example:
$(window).load(function() {
console.log('search_regulations.js firing');
//off: detach the events
//on: attach the events
$('.browse-ctp__filters-data .include, .exclude').off('click').on('click', function(){
...
}
});
In addition to what everybody were talking in the comments, I'll try to put it into an answer.
JS is event driven, that doesn't mean you have to load scripts on the fly to make them execute functions when an event is fired. A better approach would be to load everything you need on the page load, and add event listeners, this will case much better user experience(less http requests) and much better code matinability.
In general I would do something like that in you html file:
<script src="/js/main.js">
<script src="/js/search_regulations.js" async> <!-- bonus tip! google "async script tag" -->
This will load all the js you need.
Keep in mind that search_regulations.js
should add the event listener you want with jQuery on
method, but since the html didn't exist when the script added the event listener, you might want to check this.
search_regulations.js
file to be loaded, if they're on the a mobile network/the file will get bigger in size that might be a poor user experience.search_regulations.js
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With