I am trying to show the loading animation during a function call that takes some time. The function call is searching a large array that is already loaded. After the search, matching items are inserted into a table. The table is cleared prior to starting the search.
The problem is the animation only displays during the brief moment when the page updates.
Here is my code:
var interval = setInterval(function ()
{
$.mobile.loading('show');
clearInterval(interval);
}, 1);
DoSearch(term, function ()
{
var interval = setInterval(function ()
{
$.mobile.loading('hide');
clearInterval(interval);
}, 1000);
});
//The search function looks like this (detail omitted for brevity):
function DoSearch(term)
{
$("table#tableICD tbody").html('');
// also tried:
/*$("table#tableICD tbody")
.html('')
.table()
.closest("table#tableICD")
.table("refresh")
.trigger("create");*/
var tr = '';
$.each(codes, function (key, value)
{
// determine which items match and add them as table rows to 'tr'
});
$("table#tableICD tbody")
.append(tr)
.closest("table#tableICD")
.table("refresh")
.trigger("create");
callback();
}
The search works properly and adds the rows to the table. I have two unexpected behaviors:
The table does not clear until the search is complete. I have tried adding .table("refresh").trigger("create") to the line where I set the tbody html to an empty string. This does not help. (see commented line in the code)
As I mentioned, the animation displays briefly while the screen is refreshing. (Notice I set the interval to 1000 in the second setInterval function just so I could even see it.)
The suggestions I have read so far are to use setInterval instead of straight calling $.mobile.loading, which I have done and placing the search in a function and using a callback, which I have also done.
Any ideas?
Let me give you a few suggestions; they will probably not solve all your issues but they may help you found a solution.
jQuery Mobile is buggy, and for some features, we will never know were they intended to work like that or are they just plain bugs
You can call $.mobile.loading('show') on its own only in pageshow jQuery Mobile event. In any other case, you need to do it in interval or timeout.
It is better to do it in timeout, mostly because you are using less code. Here an example I made several years ago: http://jsfiddle.net/Gajotres/Zr7Gf/
$(document).on('pagebeforecreate', '[data-role="page"]', function(){
setTimeout(function(){
$.mobile.loading('show');
},1);
});
$(document).on('pageshow', '[data-role="page"]', function(){
// You do not need timeout for pageshow. I'm using it so you can see loader is actualy working
setTimeout(function(){
$.mobile.loading('hide');
},300);
});
It's difficult to enhance any jQuery Markup in real time after a page was loaded. So my advice is to first generate new table content, then clean it, and then update markup using .table("refresh").
Do table refresh only once, never do it several times in the row. It is very resourced heavy method and it will last a very long time if you run it for every row
If you are searching on keypress in the input box then showing it in the table; that is the least efficient method in jQuery Mobile. jQM is that slow, it is much better to use listview component which is least resource extensive.
Related
I have been trying to implement the autocomplete and have come across a problem that has stumped me. The first time I call .autocomplete it all works fine and I have no problems. If, however, I call it after I have removed some (unrelated) elements from the DOM and added a new section to the DOM then autocomplete does nothing and reports no errors.
Code:-
$.ajax({
type : 'get',
dataType : 'json',
url : '/finance/occupations',
cache:true,
success:function(data){
occupationList = data;
$('.js-occupation').autocomplete({
source: occupationList,
messages: {
noResults: '',
results: function(){}
},
minLength : 2,
select:function(event, ui){
$('.js-occupationId').val(ui.item.id);
}
});
}
});
The background to this page is that it contains multiple sections that are manipulated as the user moves through them. Hide and show works fine and does not impact on the autocomplete. However, if I do the following:-
var section = $('.js-addressForm:last').clone();
clearForm(section);
$('div.addressDetails').append(section);
$('.js-addressForm:first').remove();
Which gives the user the bility to add multiple addresses on the previous section then the autocomplete stops working.
Any suggestions or pointers on something obvious I am missing?
I have tried to put the initialisation of the autocomplete on an event when the element gets focus and it still does not work.
You have to create the autocomplete after all other underlying objects. If you F12, you will see that the list is "visible", however it is below and hidden by all instances created after it.
If you created a div with inputs (one input being the autocomplete), then you create the automplete then the dialog instances, the autocomplete will never show. If you create the dialog then the autocomplete, no problem. The problem is the z-order
I have faced the same issue. For now to fix this, i'm creating widget on the input once input is in focus. It will help you solve the issue.
You can look for the help on
making sure some event bing only when needed
Sample code will look like this
$( "#target" ).focus(function() {
//I don't care if you manipulated the DOM or not. I'll be cautious. ;)
(function() {
$( "#combobox" ).combobox();
$( "#toggle" ).click(function() {
$( "#combobox" ).toggle();
});
})();
// use a flag variable if you want
});
This solved my problem. Hope its the solution you were looking f
Adding my bindings to the pageinit event like so:
$('#mypage').on("pageinit", function () {
$('#login-sumbit').on('click', function () {
console.log('button clicked');
});
});
I would expect pageinit to bind the click event once only. But what happens in my single page app is that the button is binding every time the page is loaded even when clicking back.
This results in undesirable multiple duplicate binds. Any ideas on what event to use to bind only once in my single page app, so that loading the page again (back button, loading inline page) in the same session doesn't re-bind?
Looks like I found the answer myself, turns out quite rightly pageinit fires every time the page is loaded even though it's not reloading from the server, otherwise what would fire when a new page is shown.
pageinit is the right event but I need to use .one not .on, .one will bind one time only.
$('#mypage').on("pageinit", function () {
$('#login-sumbit').one('click', function () {
console.log('button clicked');
});
});
Now everything works as expected. Better still I've found you can use .one with the pageinit event for even more control over your bindings and data loads perfect for my requirements.
http://api.jquery.com/one/
You could use:
$('#login-sumbit').off('click').on('click', function(e) {
console.log('button clicked');
});
I fill list dynamically, and after that on click I have multiple calls of event. 1st time it is repeated 1 time, 2nd time 2 times, 3rd time 3 times, etc...
First, more about this problem can be found in my other answer: jQuery Mobile: document ready vs page events
Prevent multiple event binding/triggering
Because of interesting jQM loading architecture, multiple event triggering is a constant problem. For example, take a look at this code snipet:
$(document).on('pagebeforeshow','#index' ,function(e,data){
$(document).on('click', '#test-button',function(e) {
alert('Button click');
});
});
Working jsFiddle example: http://jsfiddle.net/Gajotres/CCfL4/
Each time you visit page #index click event will is going to be bound to button #test-button. There are few ways to prevent this problem:
Solution 1:
Remove event before you bind it:
$('#index').live('pagebeforeshow',function(e,data){
$('#test-button').die().live('click', function(e) {
alert('Button click');
});
});
Working jsFiddle example: http://jsfiddle.net/Gajotres/K8YmG/
In case you have different events bound to an object:
$('#index').live('pagebeforeshow',function(e,data){
$('#test-button').die('click').live('click', function(e) {
alert('Button click');
});
});
Solution 2:
Use a jQuery Filter selector, like this:
$('#carousel div:Event(!click)').each(function(){
//If click is not bind to #carousel div do something
});
Because event filter is not a part of official jQuery framework it can be found here: http://www.codenothing.com/archives/2009/event-filter/
In a nutshell, if speed is your main concern then Solution 2 is much better then Solution 1.
Solution 3:
A new one, probably an easiest of them all.
$(document).on('pagebeforeshow', '#index', function(){
$(document).on('click', '#test-button',function(e) {
if(e.handled !== true) // This will prevent event triggering more then once
{
alert('Clicked');
e.handled = true;
}
});
});
Working jsFiddle example: http://jsfiddle.net/Gajotres/Yerv9/
Tnx to the sholsinger for this solution: http://sholsinger.com/archive/2011/08/prevent-jquery-live-handlers-from-firing-multiple-times/
More info
If you want to find more about this problem take a look at this article, working examples are included.
I am using the pagecreate initialization event to call a function which makes an AJAX call to populate a list.
The problem I have is that this event never completes. The page loading message persists.
I've search here and on the Jquery forum, without any luck.
My code looks like this:
$( "#events" ).live( 'pagecreate', function(event) {
// Executed once the page is loaded
var fromDate = new Date(),
toDate = new Date(fromDate.getFullYear(), fromDate.getMonth() + 3, fromDate.getDate());
update(fromDate, toDate);
//alert('done');
});
function update(from, to) {
var eventList = $('ul#event-list');
$.ajax({
url: 'events.php',
dataType: 'json',
data: {from: from, to: to},
success: function(data) {
showEvents(data, from, to, eventList); // Create list items and append to eventList
$('.value h2').formatCurrency({ negativeFormat: "-%s%n" }); // Format currency correctly using jQuery plugin
}
});
}
I get an "a.Deferred is not a function" error, which suggests to me it has something to do with the completion of the AJAX call, but I've checked, and the showEvents function is correctly creating the list items, so it's not hanging.
After reading this, I tried alternative initialization events: pageinit, and even changePage, without success.
Thanks for your help.
p.s. in case it helps, uncommenting that alert() gets the updated list to reformat correctly, without solving the problem. I figure I'd mention it, since I obviously don't understand what's going on.
If u want to run
the code only once when your project loaded then use
mobileinit. pageshow for every view of page and pagecreate for first
time when pagecreate in your project.
I'm helping a company develop a website that utilizes jquery but I have noticed that the site slows to a complete halt with a jquery "Too Much Recursion" error. The company really needs to get this resolved but retain the slideshow capabilities as they are right now. Here is the code in question:
<script type="text/javascript">
var $testimonialCont;
var $slideshowContainer;
$(document).ready(function(){
$slideshowContainer = $('.slideshowContainer');
var inititalSlideshowDelay = setTimeout(cycle_slideshow_image, 4000);
$testimonialCont = $('.testimonialContainer');
$('.testimonialBubble').hide();
$('.testimonialBubble').removeClass('hide');
cycle_top_bubble()
var initialTestimonialDelay = setTimeout(cycle_top_bubble, 3000);
});
function cycle_slideshow_image(){
//This code cycles the slideshow caption headings and body text
$('h1.slideshowCaptionHeading:last').fadeOut(1500, function(){
$(this).prependTo('.captionHeaderArea');
$(this).show(1);
var delay = setTimeout(cycle_slideshow_image, 4000);
});
$('p.slideshowCaptionBody:last').fadeOut(1500, function(){
$(this).prependTo('.captionBodyArea');
$(this).show(1);
var delay = setTimeout(cycle_slideshow_image, 4000);
});
$('img.slideshowSlide:last').fadeOut(1500, function(){
$(this).prependTo($slideshowContainer);
$(this).show(1);
var delay = setTimeout(cycle_slideshow_image, 4000);
});
}
function cycle_top_bubble(){
$('.testimonialBubble:last').prependTo($testimonialCont).fadeIn(1500, function(){
var $this = $(this);
var thisTimer = setTimeout(function(){
$this.fadeOut(1500, function(){
var thisDelay = setTimeout(cycle_top_bubble, 3000);
})
}, 5000);
});
}
</script>
Here is the site's address: http://dbunderdevelopment.com/CRR/
If anyone has any suggestions, I would greatly appreciate it.
P.S. I did post this question before as an unregistered user and I sincerely apologize in advance for that. I can't seem to find the post in order to delete but, rest assured, it will not happen again. I know how bad repostings are on forums.
To me it looks like cycle_slideshow_image calls itself three times each time it is called... change it to this:
function cycle_slideshow_image(){
//This code cycles the slideshow caption headings and body text
$('h1.slideshowCaptionHeading:last').fadeOut(1500, function(){
$(this).prependTo('.captionHeaderArea');
$(this).show(1);
});
$('p.slideshowCaptionBody:last').fadeOut(1500, function(){
$(this).prependTo('.captionBodyArea');
$(this).show(1);
});
$('img.slideshowSlide:last').fadeOut(1500, function(){
$(this).prependTo($slideshowContainer);
$(this).show(1);
var delay = setTimeout(cycle_slideshow_image, 4000);
});
}
Also, cycle_top_bubble is being called twice initially, so it's running in two loops. remove this line:
var initialTestimonialDelay = setTimeout(cycle_top_bubble, 3000);
Another thing to consider is that when your page becomes an inactive tab in the browser, the timeouts are clamped to 1000ms (ref) so the animation may build up if you have the timeouts too short, which you don't, but it's something to keep in mind.
So you need to think about how recursion works, when you recurse in those set timeout functions you create a new scope inside the recursed function, Adding everything onto the stack without popping off the last function.
If you look at this as it is a block of memory but you never recurse which is the returning back up you continue to flood memory with more and more objects until its full. How you can solve this is pretty easy.
First recursion is the wrong approach for something that never completes, I explained why above. The recursion needs to be changed. The solution I would use is have a callback on the setTimeout but move your setTimeouts outside the scope of the calling function. This should help with the memory problem.
Other suggestions is to use a real slideshow plugin that someone else wrote... I know this may be frowned upon but why recreate the wheel when it has been done 1000 times. I recommend jQuery Cycle it is extremely fast and customizable.
Good luck!