Apply code to dynamically loaded image - javascript

I have a page where an image with a wrapping div gets loaded via ajax. I need to apply some code when that image is loaded. I cannot alter the ajax call so I cannot simply use on('success') on the ajax call.
After some time the image with the div gets reloaded the same way (it is a kind of rotation).
The structure is like this:
<div id="spot">
<div id="xyz">
<a href="http://someurl.com">
<img src="http://someimage.url" />
</a>
</div>
</div>
The "spot" div is fixed and everything inside of it gets loaded dynamically.
When I put in
$('#xyz img').on('load', myCallback);
It doesn't work, I think because when the image gets reloaded, the listener is also removed.
I tried with the delegated listener:
$('#spot').on('load', '#xyz img', myCallback);
but didn't work either, it doesn't get executed (I've put console.log inside to verify what happens).
When I type in console $('#spot #xyz > img') the console shows the correct object.
How can I run my myCallback function right after the dynamic content with the image is ready for some DOM scripting action?

If you can't get access to the AJAX request callback then your options are limited to a setInterval() timer which checks to see if the elements have been added, a MutationObserver or a load event on an img element - but be aware this will only fire after the src of the image has loaded so there will be a delay.
Here's how you can use the MutationObserver, but be aware this will only work for newer browsers:
var targetElement = document.querySelector('#container');
var observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
// perform the required actions here...
console.log(mutation.type);
});
});
observer.observe(targetElement, { attributes: true, childList: true, characterData: true });

Not the correct way but you can try this
var handle = setInterval(showImage, 500);
function showImage(e) {
var img = $('#spot').find("img");
if(img != undefined) {
if($(img).is(":visible")) {
clearInterval(handle);
handle = 0;
// your logic
}
}
}

Related

Browser Extension: How to execute a function when an element is loaded

I am working on a browser extension.
It has two parts:
popup - which contains checkboxes
content script - which contains the code to alter the CSS property
I am saving the states of checkboxes so that the next time I open the popup again the same checkboxes are marked as checked.
When I use the checkboxes they change the DOM as intended, however when I try to alter the DOM after the page is loaded, changes are not reflected. This is probably because the element on which I want to perform the operation is loaded slow and thus required operations fail.
I tried to use onload and ready but nothing worked
$('.question-list-table').on('load', function() {
browser.storage.local.get(["options"], modifyThenApplyChanges)
});
I also tried, but nothing changed.
$('body').on('load','.question-list-table', function() {
browser.storage.local.get(["options"], modifyThenApplyChanges)
});
Also, there is no visible error with the popup or content script as I test in both Google Chrome and Mozilla Firefox.
Update:
As suspected earlier, the target element is loaded slowly so I used setTimeout for 5 seconds and the script is working as intended.
Loading time is variable and I want to show my changes as early as possible everything in a consistent manner.
After going through MutationObserver as suggested by #charlietfl in the comment section, this is what I coded and works for me
// Mutation Observer
const observer = new MutationObserver(function (mutations) {
mutations.forEach(function(mutation) {
if(mutation.addedNodes.length) {
//do stuff
}
});
});
el = document.getElementsById('elementId');
if(el) {
observer.observe(el, {
childList: true // specify the kind of change you are looking for
});
}

jQuery loaded html content - Check if images are loaded and rendered

I have tabs logic that load html templates inside a wrapper. That's works fine, but I included an animation that animate height of the tab wrapper when tab is switched.
The problem is the following: When a template contains <img src="/some-image.png"> the $('#tab-content').load('template-url', function() {...}) callback function sometimes is executed before the browser show the images. And my animation is not working correctly.
Code example (jsFiddle):
var currentHeight = $contentHolder.height();
$contentHolder.load(path, function() {
$contentHolder.stop();
function animateHeight() {
var loadedContentHeight = $contentHolder.css('height', 'auto').height();
$contentHolder.height(currentHeight);
$contentHolder.animate({
height: loadedContentHeight
}, 800, 'linear');
}
animateHeight();
});
I tried to set little timeout, but it's not working every time. If I set more that 300ms timeout, It feels like tabs are changed too slow.
I tried to execute the animation when $('img').load(function() {}) is fired, but with no luck.
This bug occurs most often when the web page is fully refreshed and each tab content loading for first time.
The image load event is kind of broken. To know when images are loaded you will have to observe the DOM for changes. Then on every change, you have to fetch all the new images and add the onload event to them from the callback. To prevent checking each element every time, once they've been loaded you could mark them as such by adding a data-loaded="true" property for instance.
One way to listen to DOM changes is the MutationObserver event. This is supported by all modern browsers and IE11.
A better supported solution (IE9 and up) can be found in this answer: Detect changes in the DOM. I will not repeat it here (but it's included in the demo below).
On every DOM change first you check for images without the data-loaded attribute that are already loaded anyway (this could happen when an image was still in the browser's cache) by checking element.complete. If so, fire the callback function and add the attribute to it.
If .complete is not the case, add an onload event to them that also fires the callback once it is loaded.
In your case you only want to fire your callback when all images are loaded, so I added a check if there's still images without the data-loaded attribute. If you remove that if-clause your callback would run after each image is loaded.
// Observe the DOM for changes
observeDOM(document.body, function(){
checkNewImages();
});
var checkNewImages = function() {
var images = $('img:not([data-loaded]').each(function() {
addImageLoadedEvent( this );
});
}
var addImageLoadedEvent = function(img) {
if (img.complete) {
onImageLoaded(img);
} else {
$(img).on('load', function() {
onImageLoaded(this);
});
}
}
// The callback that is fired once an element is loaded
var onImagesLoaded = function(img) {
$(img).attr('data-loaded', 'true');
if($('img:not([data-loaded])').length === 0) {
// YourCallbackHere();
}
}
DEMO: fire event on all images loaded
You can call your animateHeight function as each image in the loaded HTML is in turn loaded. You can expand this selection if you have other objects like videos.
// Call animateHeight as each image loads
var items = $('img', $contentHolder);
items.bind('load', function(){
animateHeight();
});
Updated demo: http://jsfiddle.net/jxxrhvvz/1/

What is alternative for onload for <a>?

I have this HTML:
<a onmouseover="coverLookout(this,0)" style="width:100%;overflow:hidden" href="#jtCr4NsVySc" class="youtube">
<img id="video_642" style="width:100%;margin-left:0;max-width:100%;visibility:visible" src="https://img.youtube.com/vi/jtCr4NsVySc/mqdefault.jpg">
<span></span>
</a>
And js:
var yt=new Array('maxresdefault.jpg','mqdefault.jpg');
var coverLookout = function(block,i){
console.log(i);
var code=$(block).attr('href').toString().substr(1);
var url = "https://img.youtube.com/vi/" + code + "/" + yt[i];
$(function($){
$.ajax({
url: "function_js.php?page=check_img",
data: {"url":url},
cache: false,
success: function(response){
console.log(response);
if(response=="200"){
$(block).find("img").attr('src',url);return;
}else{
coverLookout(block,++i);
}
}
});
});
};
How can I use coverLookout function while *a* is loading instead of onmouseover? *A* onload doesn't work because onload I can use only with *body*. But how do onload for other tags?
A onload doesn't work because onload I can use only with body. But how do onload for other tags?
No, it works for elements that have a load event. a doesn't have a load event because it never has something to load. All a content is inline. load relates to things like images and scripts and stylesheets and such, that load a separate resource.
img has a load event, if you're talking about the image inside the link loading. You have to be sure to hook the event before setting the img source (the onload attribute works), or check the complete flag on the element if hooking the event later to see if it's already done.
From the comments below, it sounds like you want to change the img's source when the link is clicked. You can do that like this:
<a onclick="document.getElementById('video_642').src = '/new/src/here.png' ...
...or as you're using jQuery, this will handle all youtube links:
$("a.youtube").click(function() {
$(this).find("img").attr("src", "/new/src/here.png");
});
But the user may not see it, because when the link is followed, the browser tears down the page right away. You can improve the chances your user will see it if you make sure it's already in cache, by putting it on your page somewhere but hidden:
<img style="display: none" src="/new/src/here.png">
...so the browser has it in cache to display it before tearing down the page.
I want to while page is loading src in img will be changed by script
You mean when the page these links are on is loading? Okay. This would go in a script tag at the end of the body (just before the closing </body> element:
// Find all images within a.youtube links and change their `src`
$("a.youtube img").each(function() {
var img = $(this);
// Save current src so we can put it back later
img.attr("data-real-src", this.src);
// Set new source
this.src = "/new/src/here.png";
});
// Then when the page is done loading...
$(window).on("load", function() {
// Restore the original `src` values on the images
$("img[data-real-src]").each(function() {
var img = $(this);
img.attr("src", img.attr("data-real-src")).removeAttr("data-real-src");
});
});
onload Function works only with <body> so after document loading (under $(document).ready() function collect all a tags and call the function based on their index.
var els = document.getElementsByTagName("a");
for(var i = 0, l = els.length; i < l; i++) {
coverLookout(els[i],i);
}

Running a check on content loaded in via a .load() function

had this niggling issue that i cant seem to figure out.
I have a blog post on a CMS that i am building and there is some content saved into a div with it own unique ID. When the user clicks an edit button, a CKeditor is shown (containing the same text as the div). I also display a save button which when clicked, calls the processing PHP script via AJAX.
On a database update success, i use this in my AJAX call:
if (response.databaseSuccess) {
$("#container #" +response.postid).load("#container #" +response.postContentID);
}
This works perfectly and loads the updated content into the div.
Now the issue...
On page load i use this:
$(document).ready(function () {
// check each image in the .blogtest divs for their width. If its less than X make it full size, if not its poor and keep it normal
function resize() {
var box = $(".blogtest");
box.find("img.buildimage").on('load', function () {
var img = $(this),
width = img.width();
if (width >= 650) {
img.addClass("buildimage-large");
} else if (width < 500 && width > 101) {
img.addClass("buildimage-small");
}
// if image is less than X, its most likely a smiley
else if (width < 100) {
img.addClass("buildimage-smiley");
}
}).filter(function () {
//if the image is already loaded manually trigger the event
return this.complete;
}).trigger('load');
}
resize();
});
This works, and checks the images for their width and acts accordingly. After the page has fully loaded the images correctly get given their new class which changes their width.
The problem is that i cannot get this function to work on the data that is saved. So when i click save and the content is loaded via .load(), the new images are not checked.
I have tried adding the above function into the AJAX success return but it doesnt do anything.
Any ideas?
If you are trying to hook into the onload event for images that have already been added to the page, it is very easy to miss the onload event, particularly if the image is already in the browser cache (and thus will load quickly) as the onload event may have already fired before you get a chance to attach your event handler. The usual work-around is to do something like this where you check to see if it's already loaded before attaching an onload handler:
box.find("img.buildimage").each(function() {
if (this.complete) {
// image already loaded so just process it here
} else {
// image not yet loaded so attach an onload handler
$(this).on("load", function() {
// now the image is loaded so process it here
});
}
});
I'm not sure exactly what code you're using to dynamically load new content. If you're doing that with Ajax, you need to make sure you don't fire the above code until after the content has been added to the page (the success or completion handler of whatever load operation you're using).
So, if this is where you're loading new content:
if (response.databaseSuccess) {
$("#container #" +response.postid).load("#container #" +response.postContentID);
}
then, you would use a completion handler callback on the .load() function to trigger the above code:
if (response.databaseSuccess) {
$("#container #" +response.postid).load("#container #" +response.postContentID, function() {
// code here that looks at the dynamically loaded content
});
}

Exactly when does an IFRAME onload event fire?

Does the IFRAME's onload event fire when the HTML has fully downloaded, or only when all dependent elements load as well? (css/js/img)
The latter: <body onload= fires only when all dependent elements (css/js/img) have been loaded as well.
If you want to run JavaScript code when the HTML has been loaded, do this at the end of your HTML:
<script>alert('HTML loaded.')</script></body></html>
Here is a relevant e-mail thread about the difference between load and ready (jQuery supports both).
The above answer (using onload event) is correct, however in certain cases this seems to misbehave. Especially when dynamically generating a print template for web-content.
I try to print certain contents of a page by creating a dynamic iframe and printing it. If it contains images i cant get it to fire when the images are loaded. It always fires too soon when the images are still loading resulting in a incomplete print:
function printElement(jqElement){
if (!$("#printframe").length){
$("body").append('<iframe id="printframe" name="printframe" style="height: 0px; width: 0px; position: absolute" />');
}
var printframe = $("#printframe")[0].contentWindow;
printframe.document.open();
printframe.document.write('<html><head></head><body onload="window.focus(); window.print()">');
printframe.document.write(jqElement[0].innerHTML);
printframe.document.write('</body></html>');
// printframe.document.body.onload = function(){
// printframe.focus();
// printframe.print();
// };
printframe.document.close();
// printframe.focus();
// printframe.print();
// printframe.document.body.onload = function()...
}
as you can see i tried out several methods to bind the onload handler... in any case it will fire too early. I know that because the browser print preview (google chrome) contains broken images. When I cancel the print and call that function again (images are now cached) the print preview is fine.
... fortunately i found a solution. not pretty but suitable. What it does that it scans the subtree for 'img' tags and checking the 'complete' state of those. if uncomplete it delays a recheck after 250ms.
function delayUntilImgComplete(element, func){
var images = element.find('img');
var complete = true;
$.each(images, function(index, image){
if (!image.complete) complete = false;
});
if (complete) func();
else setTimeout(function(){
delayUntilImgComplete(element, func);}
, 250);
}
function printElement(jqElement){
delayUntilImgComplete(jqElement, function(){
if (!$("#printframe").length){
$("body").append('<iframe id="printframe" name="printframe" style="height: 0px; width: 0px; position: absolute" />');
}
var printframe = $("#printframe")[0].contentWindow;
printframe.document.open();
printframe.document.write(jqElement[0].innerHTML);
printframe.document.close();
printframe.focus();
printframe.print();
});
}
Just when the html loads, not the dependent elements. (or so I think).
To fire when the rest of the page loads do jQuery(window).load(function(){ or window.onload not document onready.
You can also check to see if an image element is loaded and there... if image . load-- etc.

Categories