Problem:
I am having trouble implementing a recursive image lazy load in all relevant versions of Internet Explorer. I am using jQuery 1.3.2, and the code that follows works wonderfully on Firefox, Safari, and Chrome.
While I would expect that IE6's javascript engine would choke, I am very surprised to find that it does not work at all on IE7, and only occasionally on IE8. That it works sometimes on IE8 is frustrating, because it seems to imply that if I work hard enough and set enough breakpoints in the Microsoft script debugger, I'll perhaps get it to work after some struggle.
I'm aware that I don't have to do this recursively, and I will reimplement it if I don't find a suitable solution, but the recursive approach is particularly suitable in this example because I would like the images to load one at time, prettily in a row. (And I expect a max depth of around 15)
I've come to StackOverflow with this question because I've run up against this a problem like this in the past and would like to know if anyone has any insights into what the problem may be:
recursion in jQuery?
recursion in IE[6-8]'s javascript engine?
faulty jQuery callbacks/methods-chaining in IE[6-8]?
naive implementation?
Code:
Here is the lazy-load function:
jQuery.lazyLoadImages = function(imgSelector, recursive, fadeIn)
{
var image = $(imgSelector);
if (image.size()) {
image.parents(SAH.imageContentSelector).addClass(SAH.loadingClass);
// the img src attribute is stored in the alt attribute
var imgSrc = image.attr('alt');
image.attr('src', imgSrc).attr('alt','').load(function() {
$(this)
.removeClass(SAH.lazyLoadClass)
.parents(SAH.imageContentSelector)
.removeClass(SAH.loadingClass);
if (fadeIn) $(this).fadeIn(SAH.lazyLoadDuration);
if (recursive) {
var nextPos = eval(parseInt(imgSelector.replace(/.*position-(\d+).*/,'$1')) + 1);
var nextImage = imgSelector.replace(/position-(\d+)/,'position-' + nextPos);
$.lazyLoadImages(nextImage, recursive, fadeIn);
}
});
return true;
} else {
return false;
}
}
The SAH.* variables are just variables stored in a global object, SAH. Here is the relevant section that calls $.lazyLoadImages():
// fade the first image in with the navBar
var firstGalleryImageSelector = 'img#img-position-1-' + galleryId + '.' + SAH.lazyLoadClass;
$.lazyLoadImages(firstGalleryImageSelector,false,true);
navBar.show('slide', { direction: 'right' }, function() {
// load the rest after the navBar callback
$.lazyLoadImages(firstGalleryImageSelector.replace(/position-1/,'position-2'), true, true);
});
The first call to $.lazyLoadImages() is non-recursive; the second one is recursive and fires after a navigation bar slides into the window.
Finally, here is the relevant html:
<div id='position-1-i4design' class='content image' style='width:400px'>
<div class='image-gallery'>
<a class='local-x' href='#position-1-i4design'>
<img alt='/images/press/i4design/i4design-1.jpg' id='img-position-1-i4design' class='lazy-load hide'>
</a>
...
</div>
...
</div>
<div id='position-2-i4design' class='content image' style='width:389px'>
<div class='image-gallery'>
<a class='local-x' href='#position-2-i4design'>
<img alt='/images/press/i4design/i4design-2.jpg' id='img-position-2-i4design' class='lazy-load hide'>
</a>
...
</div>
...
</div>
<div id='position-3-i4design' class='content image' style='width:398px'>
<div class='image-gallery'>
<a class='local-x' href='#position-3-i4design'>
<img alt='/images/press/i4design/i4design-3.jpg' id='img-position-3-i4design' class='lazy-load hide'>
</a>
...
</div>
...
</div>
...
The IEs need onload Events defined for images before you attempt to set that Element's src. All other browsers will handle that just fine; the IEs will choke.
It's likely that your load function in the above will never run for that reason.
Give this a try:
image.attr('alt','').load(function() {
$(this)
.removeClass(SAH.lazyLoadClass)
.parents(SAH.imageContentSelector)
.removeClass(SAH.loadingClass);
if (fadeIn) $(this).fadeIn(SAH.lazyLoadDuration);
if (recursive) {
var nextPos = eval(parseInt(imgSelector.replace(/.*position-(\d+).*/,'$1')) + 1);
var nextImage = imgSelector.replace(/position-(\d+)/,'position-' + nextPos);
$.lazyLoadImages(nextImage, recursive, fadeIn);
}
}).attr('src', imgSrc);
I have done something very similar with a recursive JavaScript function to load images, which works fine in IE.
The major differences that I can see are:
I used a normal JavaScript function, not a jQuery function.
I created each image with jQuery and added it to the relevant container.
I'm not sure if either of those points matters, but just looking at your code, I'm guessing that some more expensive functions would be referencing the image's parents twice, and also fading the image in.
Does it successfully run if those are commented out?
Related
I am trying to make a HTML element (in this case an object) scroll at the same time I scroll another element (div). So far I have tried multiple ways but cannot seem to get the element I want to move automatically to do so.
Here are a couple of ways I am trying to do this;
$(document).ready(function () {
$("canvas").scroll(function () {
$("FrameStyle").scrollTop($("canvas").scrollTop);
});
});
function elementOnScroll() {
var a = document.getElementById('canvas').scrollTop;
document.getElementById('FrameStyle').scrollTop = a;
}
Neither of these have had any affect on the scroll bar that should be moving and i'm not sure why now.
HTML:
<asp:Literal ID="litWebsiteFrame" runat="server"></asp:Literal>
<div id="canvas">
<canvas id="DrawingCanvas">
<p>
Unfortunately, your browser is currently unsupported by our web application. We are sorry for the inconvenience. Please use one of the supported browsers listed below.
</p>
<p>
Supported browsers: Chrome,
Opera, Firefox,
Safari, and Konqueror.
</p>
</canvas>
</div>
The literal is just the following html code:
"<object data='" + qa.GetURL + "' id='FrameStyle'></object>"
You have missed the selector symbol. FrameStyle and canvas is ID. You need to use the script like below.
$("#FrameStyle").scrollTop($("#canvas").scrollTop);
Problem- I am displaying some images on a page which are being served by some proxy server. In each page I am displaying 30 images ( 6 rows - 5 in each row). Here if due to overload or due to any other issue if proxy server could not able to server images( either all images or some of them) in 6 seconds then I want to replace unloaded image url with some other url using javascript so that I could display 30 images at the end.
What I tried is below.
objImg = new Image();
objImg.src = 'http://www.menucool.com/slider/prod/image-slider-4.jpg';
if(!objImg.complete)
{
alert('image not loaded');
}else{
img.src = 'http://www.menucool.com/slider/prod/image-slider-4.jpg';
}
I also tried with below code.
$('img[id^="picThumbImg_"]').each(function(){
if($(this).load()) {
//it will display loaded image id's to console
window.console.log($(this).attr('id'));
}
});
I could not use set time-out for each image because it will delay all page load.
I checked other similar question on stack-overflow but no solution worked me perfectly as I need to display multiple images.Please guide how to proceed.
You don't have to wait 6 seconds, or using TimeOut. You can check if the images are loaded or not using the onload Javascript/Jquery event. I know, it will take a little bit to dispatch the onerror event, let see:
Why don't use the load Jquery event on the window or the image itself?
$(window).load(function(){
//check each image
})
Disadvantage:
It will wait for other resources like scripts, stylesheets & flash, and not just images, which may or may not be OK to you.
If the image loads from the cache, some browsers may not fire off the event (included yours and that's why your code is not working)
Why don't use the error Jquery event on the image itself?
$('img[id^="picThumbImg_"]').error(function(){
//image loading error
})
Disadvantages:
It doesn't work consistently nor reliably cross-browser
It doesn't fire correctly in WebKit if the image src is set to the same src as before
It doesn't correctly bubble up the DOM tree
Can cease to fire for images that already live in the browser's cache
Note:: Error is almost the same that the load event
Improving the code!:
$('img[id^="picThumbImg_"]').one('error', function() {
// image load error
}).each(function() {
if(!this.complete) $(this).error();
});
This will avoid few things of the previous code, but you still will have to wait if it's a 404 and you're replacing it in the onerror event, that will take a little bit right?
So, what now!
You can use this awesome plugin!. Once you add the reference, you just have to use something like:
var imgLoad = imagesLoaded('#img-container');
imgLoad.on( 'always', function() {
// detect which image is broken
for ( var i = 0, len = imgLoad.images.length; i < len; i++ ) {
if(!imgLoad.images[i].isLoaded){
//changing the src
imgLoad.images[i].img.src = imgLoad.images[i].img.getAttribute("data-src2");
}
}
});
Your HTML markup should look like:
<div id="img-container">
<div class="row">
...
</div>
<div class="row">
<img src="original-path.jpg" data-src2="alternative-path.jpg">
...
</div>
<div class="row">
...
</div>
</div>
Note: You don't need jQuery in this case and this plugin is suggested by Paul Irish ;)
Give all your images a specific class. Loop through your images and use .load() to check if loaded, example below...
Detect image load
I´am trying to get an simple imagebutton to work in Phonegap. I wanna swap image when clicked and forward to location after a short time.
So what i have tried:
function highl(Bildname,BildURL,Link) {
document.images[Bildname].src = BildURL;
window.setTimeout(forward,1000);
function forward() {
window.location = Link;
}
}
in HTML just links like:
<img name="level01" src="level1.png" border="0">
Works well in my Moz, but not in Webkit/phonegap (swap doesen´t work forward is well).
Can anybody help?
edit: also doesen´t work in chrome...
Webkit doesn't support DOM attribute mutation (see issue 8191) marked won't fix.
There might be a link with your issue.
As a workaround, I think you should simply remove the content of the DOM node, and create a new image node instead.
Edit: with code
You need to identify the container.
Also, I set href, so that I javascrpt is disabled, the link can still be followed.
If javascript is enabled, return false tells the browser not to follow the link.
<a href="test.html" onClick="return highl(this, 'level1h.png', 'test.html');">
javascript. I have inlined forward because it was very short, but you don't need to.
function highl(el, imgURL, link) {
var img = new Image();
img.src = imgUrl;
// remove current image. TODO ensure firstChild is not null?
el.removeChild(el.firstChild);
// place new image
el.append(img);
setTimeout(function() {window.location=link;}, 1000);
return false;
}
Quite a simple question, yet it has been bugging me all week!
Firstly, I do not expect someone to write me this huge piece of code, then me take it away and claim it for my own. Would prefer someone to actually help me write this :)
I am attempting to show a playlist on my website as a png image.
I have 2 playlists that must be shown.
The playlist will change on an image press.
I have 4 button images, 'CD1up', 'CD1down', 'CD2up' and 'CD2down'.
I would like to have these buttons changing what current playlist is being shown, but also showing the buttons correct state. For example, is playlist1 is being shown, then 'CD1up' must be shown, and 'CD2down' shown.
I would post my current code here, but I basically scrapped it all and decided to start from scratch since I'm terrible with web javascript.
All help is greatly appreciated!
I can basically fluent in HTML and CSS, but horrible at web javascript.
Some notes:
If you give each image an id attribute, you can use document.getElementById to get a reference to that element once the page is loaded.
Then you can set the src property on that element to a new URL to change the image.
Make sure your script tag is after the elements in the HTML (just before the closing </body> works) so that the elements exist when you want them.
You can add a click event handler to any element on the page. Most browsers support addEventListener but some older versions of IE still require you to use attachEvent to hook up the handler. So you see people with functions that look something like this:
function hookEvent(element, eventName, handler) {
if (element.addEventListener) {
element.addEventListener(eventName, handler, false);
}
else if (element.attachEvent) {
element.attachEvent("on" + eventName, handler);
}
else {
element["on" + eventName] = function(event) {
return handler.call(this, event || window.event);
};
}
}
So for example, if you have this img:
<img id="myImage" src="/path/to/img.png">
This cycles through four images on click:
<!-- This must be AFTER the `img` above in the HTML,
just before your closing /body tag is good -->
<script>
(function() {
var myImage = document.getElementById("myImage"),
images = [
"/path/to/img1.png",
"/path/to/img2.png",
"/path/to/img3.png",
"/path/to/img4.png"
],
index = -1;
hookEvent(myImage, "click", imageClick);
function imageClick() {
++index;
if (index >= images.length) {
index = 0;
}
myImage.src = images[index];
}
})();
</script>
You can get a lot of utility functionality and smooth over browser differences using a decent library like jQuery, YUI, Closure, or any of several others, although if all you want to do on the page is change the images sometimes and handle a click or two, that might be overkill.
I have a slightly vague question. I have the following in my code: http://jsfiddle.net/PMnmw/2/
In the jsfiddle example, it runs smoothly. The images are swapped quickly and without any hassle. When it is in my codebase though, there is a definite lag.
I'm trying to figure out why that lag is happening. The structure of the jquery is exactly the same as above. I.e. Inside the $(document).ready (...) function, I have a check to see if the user clicked on the img (based on the classname) and then I execute the same code as in the jsfiddle.
I'm at my wits end trying to figure out what to do here... Clearly I'm not doing the swap right, or I'm being very heavy handed in doing it. Prior to this, one of my colleagues was using AJAX to do the swap, but that seems to be even more heavy duty (a full fledged get request to get the other icon...).
I've modified your fiddle: http://jsfiddle.net/PMnmw/12/
Things I've optimized:
Created a variable for both img1 and img2, so that you won't have to navigate the DOM to reference those two images anymore, thusly improving performance.
Applied a click handler to the images themselves, so you don't have to search the children of the wrapper.
The basic idea was to reduce the number of jquery selections as much as possible.
Let me know if this helped speed things up.
$(document).ready(function() {
var img1 = $('#img1');
var img2 = $('#img2');
$(".toggle_img").click(function(e) {
var target = $(e.target);
if(target.is(img1)){
img1.hide();
img2.show();
}
else if (target.is(img2)) {
img2.hide();
img1.show();
}
});
});
Images that are not visible are normally not loaded by the browser before they are made visible. If there seems to be a problem, start by downloading an image optimizer like RIOT or pngCrush to optimize your images.
If it's only two arrows, you should consider joining them into a CSS sprite.
You could try not doing everything with jQuery, but it shouldn't really make that much difference.
Something like this maybe, with the hidden image loaded in JS and some traversing done outside jQuery (but that is probably not the problem, although the code seems overly long for a simple image swap?) :
$(document).ready(function() {
var img=new Image();
img.src='http://i.imgur.com/ZFSRC.png'; //hidden image url
$(".wrapper").click(function(e) {
if(e.target.className=='toggle_img') {
$('.toggle_img').toggle();
if (e.target.parentNode.childNodes[1].style.display=='none') {
console.log("hello");
} else {
console.log("goodbye");
}
}
});
});
FIDDLE