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

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.

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/).

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/

How to debug DOM manipulation by jQuery?

I'm working on an website with some dynamic jQuery content.
If the user pushed a button ("show menu") on the page, an javascript function runs. Let this function call loadMenu().
The loadMenu() function loads a menu (web conent) from server using ajax. Part of this loaded code is javascript/jquery. 2 functions of this code make some elements on the page draggable, 2 other functions make some elements on the webpage droppable. These functions are all started at $.ready-Time (if the DOM is ready).
All this works fine.
Now i added an "MenuAlwaysVisible" feature. This means: if the web-page is loading and finished (ready) the user doesn't need to press the button "show menu", because the javascript loadMenu() now fires automatically, if the page is ready
The problem now is, it looks like, the draggable handler are attached and worked as defined, but droppable does not work.
I'm not sure, but probably the droppable function runs on a time, where the DOM elements doesn't like to be droppable? Ore maybe some other jQuery codes overrides this? (but there are no other droppable elements on the page)?
So the question is: how to analyze that problem: how to debug DOM manipulation, using Windows and Firefox/Firebug or Safari, Chrome .. whatever...
Thank you!
One debugging trick I have found endlessly useful for dealing with JQuery is the insert obvious code trick. Slap in a .hide() command on some obvious, identifiable part of the page, and see if the code ever runs. Lets you track which code pieces are not behaving as intended, and which are simply never being used in the first place.
To answer my own question: i did not found any alternatives way than using firebug and console.info() or console.warn() to debug the code.
Thanks # all for the comments

Is $.empty() enough for big ajaxy apps?

Been working on an App and since it's getting a bit too big I've thinking of ways to improve memory management since the app runs mostly on Javascipt. So every time a navigation item is clicked I would call the jquery empty then show the html via ajax. ex:
//$.ajaxSetup(); called before this
//$this is the attached element
$.ajax({success:function(data){
$this.empty().html(data.output).fadeIn(400);
//more javascript stuff like loading tinymce or jquery ui
}});
is this enough to prevent memory leaks? I'm not entirely sure what empty does but I'm assuming it removes all DOM elements within that div along with any other objects and events? btw. You can find the app here http://webproposalgenerator.com/ and http://webproposalgenerator.com/demo.
any tips on improving the performance/security or any feedback at all would be greatly appreciated.
$.fn.empty should be enough, it deletes all data and events associated to the elements and then deletes the elements. It also calls .widget("destroy") on all jquery-ui widget.js based widgets that are defined on those elements.
It is also important to note that jquery's $.fn.html method calls $.fn.empty() on the given element before appending html, therefore, if you are using $.fn.html, you don't have to call $.fn.empty
actually my guess was that .html implies .empty anyway, also I'm not sure that's true. for the perforamnce part: according to jqfundamentals excelent book it is a recommanded best practice to add content while the element is in .detach() from the DOM. tried to lock at the code for advice but didn't find it. nice site btw

jQuery event handlers not firing in IE

I have a list of items on a page with a set of controls to MoveUp, MoveDown and Delete.
The controls sit at the top of list hidden by default. As you mouseover an item row, I select the controls with jquery
//doc ready function:
..
var tools = $('#tools');
$('#moveup').click(MoveUp);
$('#movedn').click(MoveDn);
$('#delete').click(Delete);
..
$('li.item').mouseover(function(){
$(this).prepend(tools);
});
This works great in Firefox .. the tools move into the current row, and the click events call the ajax functions. However, in IE6 and IE7 .. no click occurs. I tried unbinding on mouseout and rebinding on each mouseover .. to no avail.
I also looked into various reasons outside of javascript (e.g. transparent png conflicts, z-index, position:absolute) .. also no solution found.
I eventually needed to add a tools row to each item and show/hide on mouse over/out. Works just as well -- the only downer is that I have much more 'tools' markup on my page.
Does anyone know why IE ignores/drops/kills the mouse events once the objects are moved (using prepend)? And why rebinding the event afterwards also has no effect? Kept me annoyed for almost 2 hours before I gave up.
IE will lose events depending on how you are adding things to the DOM.
var ele = $("#itemtocopy");
$("#someotheritem").append( ele ); // Will not work and will lose events
$("#someotheritem").append( ele.clone(true) );
I would also recommend using .live() on the click events to simplify your code a little. Mouseover/out is not supported by live yet. http://docs.jquery.com/Events/live
I just spent the whole day troubleshooting events not triggering on items appended to the DOM, (IE7, jQuery 1.4.1) and it wasn't because I needed to use live() (though, good to know, Chad), nor was it because I needed to clone the items.
It was because I was selecting anchor tags that had a "#" in them like so:
var myitem = $('a[href=#top]');
My solution was to use the "Attribute Contains Selector" like so:
var myitem = $('a[href*=top]');
Fortunately I have enough control over everything that it won't likely break in the future. This isn't technically related to appended objects, but hopefully it saves someone some time.
i had a similar problem. trying to use .ready to load a div on the initial page load.
works well in FF , but not ie7.
i have found a hack that seems to get around this.
I have load call a callback, divLoaded().
In divLoaded i check the $('#targetdiv').innerText.length < 50 or whatever you think will indicate that it didnt load. If I detect that case, i simply call the function taht loads that div again.
Oddly enough, i also add a '.' to the innerText before i recall the ajax function. It seems taht sometimes we go through 3 or 4 loops before the ajax load finally takes.
This leads me to think that document.ready works pretty flawlessly in IE7, which seems to dispel a bit of a myth that it is unreliable. What really 'seems' to be happening is that .load is a little bit flakey and doesnt work well when the page is just loaded.
I am still a bit green w/ all the jQuery stuff, so take this w/ a grain of salt. Interested to hear anyone's take on my little hypothesis.
cheers
greg

Categories