Javascript Performance Huge page - javascript

I have an Single Page App with lots of buttons (3000 with click event) and bootstrap accordions with nested accordion. The page can be almost 1Mb of size.
My problem is that sometimes after a while, the browser becomes unresponsive and crashes. I wondered if there is something I could do to reduce the chances of this occuring or should I change the whole design?
EDIT:
For example, I'm using :
$(".plus").click(function () {
add1($(this).closest("tr"));
});
Should I use :
$(document.body).on("click", ".plus", add1($(this).closest("tr")));
Instead?
Thanks

I used the idea of delegating the click event to the parent and it seems to have improved greatly considering the number of buttons. But the main problem seems to be my design.
Thanks to Sabithpocker.
I'll also try the options of attach/detach of dom elements that aren't visible.

Related

.scrollTop(0) not working for getting a div to scroll to the top

I have a feeling what ever gave me problems that I tried to find a solution in this question - Can't trigger a jquery function with scroll on a particular div - might be responsible for scroll related issues here.
Short version: Can't get this, or anything similar, to work
$("#Container3").scrollTop(0);
Nothing happens really, no error in the console, no wierd behaviour, just seems to ignore the scrollTop(0) request.
Long version: I'm sorry but posting a code snippet isn't feasible as it's a complex app-like interface but I'll try to explain the issue to the best of my abilities:
Mobile responsive website that loads different interfaces depending on screen real-estate.
Smallest interface composed of 3 parts - navigation at the top, search at the bottom and content in the middle.
Content is mostly loaded during use and not at page load.
At the push of a button that re-loads the contents of a particular div I also need to scroll that div to the top for usability purposes.
While it doesn't seem to influence my problem (removing it doesn't solve the issue) I should disclose that I'm using hammer.js to simulate touch events as it might influence the solution.
The load is done outside the viewport so animations aren't needed but I'll take them as long as they get this to work.
This is what my jquery request looks like
$(document).on("click",".NavRowButton",function(event){
$("#Container3").scrollTop(0);
var $targetButtonId=$(event.target).attr("id");
$("#Content").load("/load/login/"+$targetButtonId+".php");
$("#DisplayContentName").html("<span class='NavColSpan'>"+$targetButtonId+"</span>");
$("#Container3").find(".WindowBorder").css("top","0");
});
#Container3 has the scroll bar and is the immediate parent of #Content.
This is a function I'm still building and is the solution for the problem I had before and also what I'm using now to help debugging this one:
document.addEventListener('scroll',function(event){
if(event.target.className==='Container'){
var $currentScroll=$(event.target).scrollTop();
console.log($currentScroll);
var $targetId=$(event.target).attr("id");
console.log($targetId);
}
},true);
Thanks in advance.
Edit: I just noticed that if I put a $(event.target).scrollTop(0); at the end of the scroll distance debugging function it actually resets the scroll so it seems that as long as the div is the event.target it works while from the outside as during the click function I might not selecting it appropriately.
Edit2: I just learned I can cache event.targets into variables and with a .get() inside the click function I'm sure I'm selecting the right element so it just leaves how the scrollTop(0) method works.
They now look like this(also had to add a condition to limit load events):
global variable:
$DivTestVar="";
click:
$(document).on("click",".NavRowButton",function(event){
var $targetButtonId=event.target.id;
if($targetButtonId != $("#DisplayContentName").html()){
$($DivTestVar).scrollTop(0);
console.log($($DivTestVar).get());
$("#Content").load("/load/login/"+$targetButtonId+".php");
$("#DisplayContentName").html($targetButtonId);
$("#Container3").find(".WindowBorder").css("top","0");
};
});
scroll debugging:
document.addEventListener('scroll',function(event){
if(event.target.className==='Container'){
$DivTestVar=event.target;
var $currentScroll=$($DivTestVar).scrollTop();
console.log($currentScroll);
var $targetId=event.target.id;
console.log($targetId);
}
},true);
If I click before scrolling the console.log($($DivTestVar).get()); returns empty but if at the first scroll it starts returning the correct DOM element. The scrollTop(0) is ignored anyway.
Edit3: I just want to leave a small update. I have since given up on the method I was trying to use here for something with a similar effect but not as user friendly as what I was trying to achieve. As such I no longer care about this personally but if you're reading this and have a similar problem I have come across this issue a couple more times to a smaller effect and I now think it's related to position:fixed; elements and how scrollTop() deals with that but I don't have the time to delve into it more so good luck and godspeed.
Did you try the pure JS version ?
document.getElementById('Container').scrollTop = 0
You have two possibilities, as far as I know.
1-Scroll the whole page until it reached the top of your #Content div position with jQuery.
2-Your #Content is inside a div with scroll, which scrollTop(0) will work for that (example: http://jsfiddle.net/zkp07abu/).

Jquery delegate not working when setting variable

I am using ajax loading for page navigation and am using delegate for the various click events required for each function etc
The problem I am having is activating a slideshow on the home page due to the way the slider is activated.
The following is what I have and obviously the load does not work as I expected it too...
$('#Content').delegate('#Home','load',function(){
swiper = new Swiper('#swiper', {
mode:'horizontal',
speed:900 ,
loop:true,
});
});
Is there a way I can get around this without implementing an if/switch statement within my navigation coding, as I would rather keep this separate.
i have also tried:
$(document/body).delegate('#Content','change',function(){....
any problems on here I have found are all related to standard problems with delegate like click events not working etc.
Thanks

Combining jQuery Isotope and Lazy Load

Have started a project using jQuery Isotope. Initially integrated with Infinite scroll, but thought it was a little clunky.
Was hoping to replace Infinite Scroll with Lazy Load, and wondered if anyone has had any luck combining the two. Any tips to get them to play nice would be great.
Thanks a mill
If you want to use isotope's sorting/filtering functions, you will need to set the failure_limit of lazyload and trigger the event with isotope's onLayout callback.
jQuery(document).ready(function($) {
var $win = $(window),
$con = $('#container'),
$imgs = $("img.lazy");
$con.isotope({
onLayout: function() {
$win.trigger("scroll");
}
});
$imgs.lazyload({
failure_limit: Math.max($imgs.length - 1, 0)
});
});
Explanation
According to the docs ( http://www.appelsiini.net/projects/lazyload )
After scrolling page Lazy Load loops though unloaded images. In loop it checks if image has become visible. By default loop is stopped when first image below the fold (not visible) is found. This is based on following assumption. Order of images on page is same as order of images in HTML code. With some layouts assumption this might be wrong.
With an isotope sorted/filtered list, the page order is certainly different from the HTML so we need to adjust our failure_limit.
As you can see we store the jQuery object so that we can use its length-1 as our failure_limit. If you're curious as to why it is length-1, it's because of the following check in lazyload's update method.
if (++counter > settings.failure_limit) {
return false;
}
Lazy load on other events
If you are not triggering your lazyloads on scroll, you will need to swap the "scroll" trigger for whichever event you are using.
Demo
http://jsfiddle.net/arthurc/ZnEhn/
I think you might have some luck using this instead : https://github.com/fasterize/lazyload
It's library independent so won't break.
Here's working code using both jquery isotope and lazyload together successfully (tested in Chrome)
http://jsfiddle.net/wN6tC/62/
In the browser console you will get console.log('loaded image') confirmation when an image is loaded, as you scroll down. Drag the jsfiddle html box to change the width and you will see the layout change dynamically.
I added the background red class so you can see how isotope alters the dom after it loads. Most of the problems while trying to set this up come from, IMHO, isotope's dom manipulation.
I hope this is enough to get you started. Have fun.
Update:
I never tested example in other browsers, and apparently IE or FF failed to work because of the HTTPS references for the javascript resources (for some odd security reason). Replacing them was all that was needed to get it working in IE and FF as seen here:
http://jsbin.com/ajoded/
and
http://jsfiddle.net/wN6tC/73/

Problem with qTip - Tips not showing because elements load after the script

I'm not very experienced with javascript, jQuery or it's plugins but usually I manage. Anyways, my client is building a site and one of its purposes is to pick up news articles from different sites and show the titles in unordered html lists. I don't have access to his code, the news articles load up rather slow(much after the site has loaded).
I'm using qTIP and the idea is that once you hover over a news title, it will generate a tooltip. This works fine in my dev environment, because I have dummy title's that are not generated from anywhere.
The problem is that once the client sets the site up in his test environment, the scripts that load the news titles into the lists are so slow, that the qTIP-script loads before there are any elements in the lists. Hence it's not aware of any <li>'s to pick up and generate tooltips from.
Is there a way make sure that ALL of the news articles are loaded before the tooltip-script
loads? I think that a simple delay in loading the script is not very smart because some of the titles seem to take longer to load than others, so the delay would have to be rather long.
See my update at the bottom
I've been working on this problem as well, and came up with a solution similar to that provided by #Gaby. The problem with #Gaby's solution is that it doesn't create the qTip until the mouseover event has happened. This means that you won't see the qTip the first time you mouseover, but will the second time. Also, it will recreate the qTip every time you mouseover, which isn't exactly optimal.
The solution I went with is this:
$("li").live('mouseover', function() {
var target = $(this);
if (target.data('qtip')) { return false; }
target.qtip(...);
target.trigger('mouseover');
});
Here's what it does:
Sets target to the li element
Returns if that li element already has a qtip
If no qtip on li, then applies qtip to it
Sends mouseover trigger again so that qtip is activated
I know this is a bit hacky, but it seems to work. Also note that the 2.0 version of qTip should support live() as an option. As far as I can tell, the current 2.0 development branch doesn't yet support it.
UPDATE:
Here's the proper way to do this, direct from the qtip developer himself on the forums:
$('selector').live('mouseover', function() {
$(this).qtip({
overwrite: false, // Make sure another tooltip can't overwrite this one without it being explicitly destroyed
content: 'I\'m a live qTip', // comma was missing here
show: {
ready: true // Needed to make it show on first mouseover event
}
});
})
So it first makes sure that you don't recreate new qtips every mouseover with "overwrite: false". Then it makes the qtip show on the first mouseover with "show: {ready: true}".
You should use the Live Events of the jQuery framework.
Binds a handler to an event (like click) for all current - and future - matched element. Can also bind custom events.
so for example you could do something like
$("li").live( 'mouseover', function(){
$(this).qTip(...);
});
ref: http://docs.jquery.com/Events/live
Not for nothing, but I just added the show:{ready:true} in my onmouseover event. That got it working in Chrome & FF.
Yeah I came up with something similar. I think someone posted a similar one on their forums as well. I changed the mouseover-event to mousemove so that the qtip activates on the first mouseover.
$('li').live('mousemove', function() {
if( !$(this).data('qtip') ) {
$(this).qtip(...)
I also agree that this is a very hacky solution, however I couldn't come up with a better one. Maybe checking and applying the qtip in the callback function that fills the li's would be better but I don't really have access to that code.

Progressive enhancement, behavior when pages are not fully loaded yet

I'm developing sites using progressive enhancement implemented completely in jQuery.
For instance, I'm adding onclick event handlers dynamically to anchor tags to play linked MP3 files "inline" using SoundManager and popping up Youtube players for Youtube links, through $(document).ready(function()).
However, if the user clicks on them while the page is loading, they still get the non-enhanced version.
I've thought about hiding the relevant stuff (via display: none, or something like that) and showing it when loaded, or putting a modal "loading" dialog, but both sound like hacks.
Any better ideas? I feel I'm missing something completely obvious here.
Regards,
Alex
I haven't tested this, but you could try live. The thinking is that you could put your click handlers outside of document.ready so they get executed right away. Since live uses event delegation to achieve it's functionality, you don't really need to wait for the DOM to be ready, and any clicks that are made while the page is loading should still be captured by the event handler.
If that doesn't work, you could try putting the Javascript script tags directly underneath whatever they need to bind. It's not pretty, but it will pretty much eliminate the problem.
Assuming you have used good judgement and people are falling for the non-enhanced version just because the delay is too long then I would use CSS to disable the controls. The CSS will load almost right away. Then using Javascript I would toggle the CSS so the controls are re-enabled.
However, my first reaction is that if the user can click it while the page is loading, then your page or connection is too slow. However, if this is seldom the case--less than 1% of the time--then you can shrug it off as long as the user can achieve his goal, that is listen to his music. I say this because once the users realizes that a better experience awaits half a second later, he will usually wait for Javascript to render and then click.
I take the opposite stance from aleemb regarding using CSS. If you use css to disable the controls, then anyone who has javascript disabled or is using accessibility software will be unable to use those controls without disabling your stylesheet entirely.
You could use a very small inline javascript right before the closing body tag to hide the elements via js really quickly. If it's inline and doesn't have to load external resources it will be very fast, generally faster than a user can click.
However, I do agree with aleemb that if your users are able to mentally process the page and make it to the control they want to click before your js is loaded, there's probably a deeper problem with the way your page is loading. Look into ways to decrease load time: compressing image files, gzipping html/css/js files, minify your javascript, combine images into sprites, etc.
I'd suggest following Paolo Bergantino's advise - event delegation is the way to go to avoid the problem altogether.
I had a similar issue where event delegation couldn't do the job - you can read about that here.

Categories