In my components template i have a container like this
<div [innerHTML]="somehtml | safeHtml"></div>
The HTML contains images with standard image tags
like
<img src="http://google.com/someimage.png"/>
I want to know when the images inside my HTML are fully loaded. How can i check for this?
P.S.: safehtml is just a sanitizer-pipe
EDIT: I am looking for an event based solution rather than check in time-intervals! Also i need a solution specificly for this container
You can try something like this. I don't love it since it's not the angular way, but since your template is a string, I hardly believe there's something better:
HTML:
<div [innerHTML]="somehtml | safeHtml" id="div-to-check"></div>
TS:
this.somehtml = `your html`;
window.setTimeout(() => { // Let's make it async to execute it in the next tick
const promises = Array.from(
document.querySelectorAll('#div-to-check img')
) // get all images
.map((img: HTMLImageElement) => {
if (img.complete) {
return Promise.resolve(); // Check if the image already loaded. Maybe it's been too fast
}
return new Promise((resolve, reject) => {
img.onload = resolve; // it resolves when it loads
img.onerror = reject; // avoids infinite waiting
});
});
Promise.all(promises)
.then(() => {
console.log('all images loaded!');
})
.catch(() => {
console.error('an image didn\'t load');
});
});
Next time please read some other questions first, and try to solve your problem yourself, before writing your own question
But here's the solution:
jQuery(window).load(function () {
alert('page is loaded');
setTimeout(function () {
alert('page is loaded and 1 minute has passed');
}, 60000);
});
Related
After the recent Chrome update my extension started to fire "Unchecked runtime.lastError: Tabs cannot be edited right now (user may be dragging a tab)" when I attempt to use chrome.tabs API.
There is not much info on this issue yet, but I believe this is a browser bug. In the meantime my extension causes the chrome tabs to switch noticeably slower, that it used to be. It now takes a couple of seconds to change the tab. So I'm looking for a workaround.
Any ideas how to fix this?
The only solution that I have found so far is to put my handlers in a timeout like this:
chrome.tabs.onActivated.addListener((activeInfo) => {
setTimeout(() => {
// The old listener handler moves here
}, 100);
});
But there must be a better way, right?
You will still get errors but at least it will work
chrome.tabs.onActivated.addListener(function(activeInfo) {getActivatedTab();});
function getActivatedTab(){
chrome.tabs.query({'active': true, 'lastFocusedWindow': true}, function (tabs) {
try{
if(tabs[0]!=undefined){
console.log(tabs[0].url);
}
}
catch(err){
setTimeout(function() {
getActivatedTab();
},100);
}
})
}
create separate function outside of chrome.tabs.onActivated.addListener.
this way work for me.
function insertScript(tab) {
chrome.tabs.get(tab.tabId, function (info) {
console.log(info);
});
}
chrome.tabs.onActivated.addListener(function (tab) {
setTimeout(function () {
insertScript(tab);
}, 500);
});
This is a bug in Chrome91, as pointed by #wOxxOm.
This patch is suggested if you are making a lot of calls to e.g. xchrome.tabs.query
const ChromeWrapper = {
chromeTabsQuery: function (params, callback) {
chrome.tabs.query(params, tabs => {
if (chrome.runtime.lastError) {
setTimeout(function () {
//console.warn("Patch for xchrome.tabs.query (Chrome 91).");
ChromeWrapper.chromeTabsQuery(params, callback)
}, 100); // arbitrary delay
} else {
callback(tabs)
}
})
}
}
And then, just replace the instances of
xchrome.tabs.query(...
with:
ChromeWrapper.chromeTabsQuery(...
...Alright,
I don't have an really better solution other than setTimeout, as I don't really see where are the message/event posted after the tab done, so what I do is also add a setTimeout to do some "trial and error".
But how I do it is to override the original chrome.tabs.get so that we can have the same experience as we use it as expected way. (And easily to delete this snippet when they finally fix it some day)
Here is my code, cheers
chrome.tabs.get = function () {
const orig_get = chrome.tabs.get.bind(chrome.tabs);
return async function (tabId) {
return new Promise(
resolve => {
var tryGet = () => {
orig_get(tabId)
.then(resolve)
.catch(() => {
// console.log("retry get");
setTimeout(() => {
tryGet(tabId);
}, 50);
})
};
tryGet();
}
)
}
}();
I try to console some message when image is fully loaded using 'load' listener, but the code did not work, how to properly write a 'load' event listener in react ? Thankyou
useEffect(() => {
window.addEventListener('load', (event) => {
const imageTest = document.querySelector('img')
const isLoaded = imageTest.complete && imageTest.naturalHeight !== 0
console.log(isLoaded)
})
}, [])
This is not how react works. You are trying to use load event within the component when everything else is already loaded within from <div id="root"></div>.
React is Single Page App. And for the whole document load happens once only :)
However for individual elements we can set onload and fire that event in componentDidMount() or in useEffect() Hook
UPDATE: For image load check you do something like. You can do this or even use useRef()
useEffect(() => {
const imageTest = document.querySelector('img');
imageTest.onload = ()=>{
// Image is loaded and your further steps :)
const isLoaded = imageTest.complete && imageTest.naturalHeight !== 0
console.log(isLoaded);
}
}, []);
There is also one more easy way to do this:
Using onLoad synthetic event right on the image element itself. Which I think should also work fine:
const ImageLoadDemo ()=> {
const handleImageLoaded =()=> {
console.log("Image was successfully loaded");
}
const handleImageErrored =()=> {
console.log("Image was not loaded successfully");
}
return (
<div>
<img
src="https://picsum.photos/200/300"
onLoad={handleImageLoaded}
onError={handleImageErrored}
/>
</div>
);
}
The way to listen image is successfully loaded in react component is just put onLoad on your <img> tag, for example :
const MyCompoent = () => {
return <img src="yourImageLink.png" onLoad={()=>{console.log('The Image is successfully loaded')} } />
}
instead console a message you can pass a function as well
I'm still new to javascript, I have this javascript problem from CS50 that is supposed to open a mailbox and clicking on an email is supposed to open the email. I think my on click part of the problem is right, but when I open my page and click on an email it doesnt call the open_mail() function.
I've solved that the problem is that the load_mailbox function for being asynchronous is beign called after the DOM finishes to load, so technically theres no div with the class email-box when the DOM finishes to load, but i don't know how to solve this problem, can someone help please.
document.addEventListener('DOMContentLoaded', function() {
// Use buttons to toggle between views
document.querySelector('#inbox').addEventListener('click', () => load_mailbox('inbox'));
document.querySelector('#sent').addEventListener('click', () => load_mailbox('sent'));
document.querySelector('#archived').addEventListener('click', () => load_mailbox('archive'));
document.querySelector('#compose').addEventListener('click', compose_email);
document.querySelector('#compose-form').addEventListener('submit', send_mail);
document.querySelectorAll('.email-box').forEach(function(box) {
box.addEventListener('click', function (){
open_mail();
})
});
// By default, load the inbox
load_mailbox('inbox');
});
function load_mailbox(mailbox) {
fetch(`/emails/${mailbox}`)
.then(response => response.json())
.then(emails => {
document.querySelector('#email-content').innerHTML = "";
emails.forEach(inbox_mail);
})
};
function inbox_mail(email) {
const element = document.createElement('div');
if (document.querySelector(`#email-${email.id}`) === null) {
element.id = (`email-${email.id}`);
element.className = ("email-box");
element.innerHTML = `<p>From ${email.sender}</p><p>${email.subject}</p><p>At ${email.timestamp}
</p>`;
document.querySelector('#email-content').append(element);
}
}
I´d say the easiest solution would be to put the addEventListener to a point after the elements with class .email-box are created, e.g in your .then function after inbox_mail ran for each email
.then(emails => {
document.querySelector('#email-content').innerHTML = "";
emails.forEach(inbox_mail);
document.querySelectorAll('.email-box').forEach(function(box) {
box.addEventListener('click', function (){
open_mail();
});
});
});
DOMContentLoaded will trigger when the DOM from the initial request/response was loaded. What you are doing in your fetch callback is called "DOM-Manipulation" as you create elements and append them to the DOM that has already been loaded.
I have a request calling up a bunch of images like so:
<a href='www.domain1.com'><img src='../image/img1.png' onerror='imgError(this);'/></a>
<a href='www.domain2.com'><img src='../image/img2.png' onerror='imgError(this);'/></a>
The problem is when the call is made some of the images (~20%) are not ready yet. They need another second.
So in js or jquery what I would like to do is on error get the images that failed, wait 1 second, then try to load those failed images again. If they fail on the 2nd try -- oh well, Im okay with that. But Im not doing this correctly... Should I not be calling a timeout inside of another method in js?
function imgError(image) {
image.onerror = "";
image.src = image;
setTimeout(function () {
return true;
}, 1000);
}
Add a cache breaker.
function imgError(image) {
image.onerror = null;
setTimeout(function (){
image.src += '?' + +new Date;
}, 1000);
}
(This assumes your image URL doesn't already have a query string, per the example. If it does, a little more work is obviously required.)
Having read other people's questions I thought
window.onload=...
would answer my question. I have tried this but it executes the code the instant the page loads (not after the images load).
If it makes any difference the images are coming from a CDN and are not relative.
Anyone know a solution? (I'm not using jQuery)
Want a one-liner?
Promise.all(Array.from(document.images).filter(img => !img.complete).map(img => new Promise(resolve => { img.onload = img.onerror = resolve; }))).then(() => {
console.log('images finished loading');
});
Pretty backwards-compatible, works even in Firefox 52 and Chrome 49 (Windows XP era). Not in IE11, though.
Replace document.images with e.g. document.querySelectorAll(...) if you want to narrow the image list.
It uses onload and onerror for brevity. This might conflict with other code on the page if these handlers of the img elements are also set elsewhere (unlikely, but anyway). If you're not sure that your page doesn't use them and want to be safe, replace the part img.onload = img.onerror = resolve; with a lengthier one: img.addEventListener('load', resolve); img.addEventListener('error', resolve);.
It also doesn't test whether all images have loaded successfully (that there are no broken images). If you need this, here's some more advanced code:
Promise.all(Array.from(document.images).map(img => {
if (img.complete)
return Promise.resolve(img.naturalHeight !== 0);
return new Promise(resolve => {
img.addEventListener('load', () => resolve(true));
img.addEventListener('error', () => resolve(false));
});
})).then(results => {
if (results.every(res => res))
console.log('all images loaded successfully');
else
console.log('some images failed to load, all finished loading');
});
It waits until all images are either loaded or failed to load.
If you want to fail early, with the first broken image:
Promise.all(Array.from(document.images).map(img => {
if (img.complete)
if (img.naturalHeight !== 0)
return Promise.resolve();
else
return Promise.reject(img);
return new Promise((resolve, reject) => {
img.addEventListener('load', resolve);
img.addEventListener('error', () => reject(img));
});
})).then(() => {
console.log('all images loaded successfully');
}, badImg => {
console.log('some image failed to load, others may still be loading');
console.log('first broken image:', badImg);
});
Two latest code blocks use naturalHeight to detect broken images among the already loaded ones. This method generally works, but has some drawbacks: it is said to not work when the image URL is set via CSS content property and when the image is an SVG that doesn't have its dimensions specified. If this is the case, you'll have to refactor your code so that you set up the event handlers before the images begin to load. This can be done by specifying onload and onerror right in the HTML or by creating the img elements in the JavaScript. Another way would be to set src as data-src in the HTML and perform img.src = img.dataset.src after attaching the handlers.
Here is a quick hack for modern browsers:
var imgs = document.images,
len = imgs.length,
counter = 0;
[].forEach.call( imgs, function( img ) {
if(img.complete)
incrementCounter();
else
img.addEventListener( 'load', incrementCounter, false );
} );
function incrementCounter() {
counter++;
if ( counter === len ) {
console.log( 'All images loaded!' );
}
}
Once all the images are loaded, your console will show "All images loaded!".
What this code does:
Load all the images in a variable from the document
Loop through these images
Add a listener for the "load" event on each of these images to run the incrementCounter function
The incrementCounter will increment the counter
If the counter has reached the length of images, that means they're all loaded
Having this code in a cross-browser way wouldn't be so hard, it's just cleaner like this.
Promise Pattern will solve this problem in a best possible manner i have reffered to when.js a open source library to solve the problem of all image loading
function loadImage (src) {
var deferred = when.defer(),
img = document.createElement('img');
img.onload = function () {
deferred.resolve(img);
};
img.onerror = function () {
deferred.reject(new Error('Image not found: ' + src));
};
img.src = src;
// Return only the promise, so that the caller cannot
// resolve, reject, or otherwise muck with the original deferred.
return deferred.promise;
}
function loadImages(srcs) {
// srcs = array of image src urls
// Array to hold deferred for each image being loaded
var deferreds = [];
// Call loadImage for each src, and push the returned deferred
// onto the deferreds array
for(var i = 0, len = srcs.length; i < len; i++) {
deferreds.push(loadImage(srcs[i]));
// NOTE: We could push only the promise, but since this array never
// leaves the loadImages function, it's ok to push the whole
// deferred. No one can gain access to them.
// However, if this array were exposed (e.g. via return value),
// it would be better to push only the promise.
}
// Return a new promise that will resolve only when all the
// promises in deferreds have resolved.
// NOTE: when.all returns only a promise, not a deferred, so
// this is safe to expose to the caller.
return when.all(deferreds);
}
loadImages(imageSrcArray).then(
function gotEm(imageArray) {
doFancyStuffWithImages(imageArray);
return imageArray.length;
},
function doh(err) {
handleError(err);
}
).then(
function shout (count) {
// This will happen after gotEm() and count is the value
// returned by gotEm()
alert('see my new ' + count + ' images?');
}
);
Using window.onload will not work because it fires once the page is loaded, however images are not included in this definition of loaded.
The general solution to this is the ImagesLoaded jQuery plugin.
If you're keen on not using jQuery at all, you could at least try converting this plugin into pure Javascript. At 93 significant lines of code and with good commenting, it shouldn't be a hard task to accomplish.
You can have the onload event on the image that can callback a function that does the processing... Regarding how to handle if all images are loaded, I am not sure if any of the following mechanisms will work:
have a function that counts the number of images for which onload is called, if this is equal to the total number of images on your page then do your necessary processing.
<title>Pre Loading...</title>
</head>
<style type="text/css" media="screen"> html, body{ margin:0;
padding:0; overflow:auto; }
#loading{ position:fixed; width:100%; height:100%; position:absolute; z-index:1; ackground:white url(loader.gif) no-repeat center; }**
</style>
<script> function loaded(){
document.getElementById("loading").style.visibility = "hidden"; }
</script>
<body onload="loaded();"> <div id="loading"></div>
<img id="img" src="avatar8.jpg" title="AVATAR" alt="Picture of Avatar
movie" />
</body>
I was looking for something like this, if you won't mind using setInterval this code is easy and straightforward. In my case I'm okay to use setInterval because it will run maybe 4-5 times.
const interval = setInterval(() => {
const allImagesLoaded = [...document.querySelectorAll('img')]
.map(x => x.complete)
.indexOf(false) === -1;
if (allImagesLoaded) {
window.print();
clearInterval(interval);
}
}, 500);
I was about to suggest the same thing Baz1nga said.
Also, another possible option that's maybe not as foolproof but easier to maintain is to pick the most important/biggest image and attach an onload event to only that one.
the advantage here is that there's less code to change if you later add more images to your page.
This works great:
$(function() {
$(window).bind("load", function() {
// code here
});
});