.appendChild and window.print(); - javascript

I have the following code:
const printString = // a long string w/ several base64 encoded images;
const printContainer = document.createElement('div');
printContainer.innerHTML = printString;
document.body.appendChild(printContainer);
window.print();
printString is a long string with several largeish base64 encoded images included. The string gets set as the innerHTML of the printContainer and then the whole thing gets printed.
This works okay, but on the initial load of the page, it apparently takes the browser a moment to render the base64 encoded images and in that time, window.print() goes ahead and fires, before all the images have actually loaded into the DOM.
That is, window.print() can fire before .innerHTML has finished rendering the new element.
If I add a brief delay to the window.print(), then everything works fine. Like so:
const printString = // a long string w/ several base64 encoded images;
const printContainer = document.createElement('div');
printContainer.innerHTML = printString;
document.body.appendChild(printContainer);
setTimeout(() => {
window.print();
}, 100);
This isn't a great solution, however, and I would really like to find a solution along the lines of "you just wait until .innerHTML() is actually finished, window.print();
All of this is tested in Chrome, so far.
Any ideas appreciated!
Edit: an answer
This is a modest reworking of #Keith's answer below.
const imgs = document.querySelectorAll('img.images-in-question');
function checkDone() {
if (ready === imgs.length) {
// do stuffs
}
}
function incrementReady(){
ready++;
checkDone();
}
for (const img of imgs) {
if (img.complete) ready++;
else {
img.addEventListener('load', incrementReady);
}
}
checkDone();

Below is a simple script to wait for all images to load.
It basically does a querySelectAll to get all the images, and then attaches the onload event, when the amount of images loaded is equal to the amount of images in the list, everything is then loaded.
This then will of course work with both external URL, and data uri's..
Update, noticed a slight issue with my original image load check, in Chrome sometimes the onload is not fired, I assume it's because if it can pull the resources from cache, it might get loaded before the onload event is even attached, as such I've added a check for the complete flag first. This seems to fix the issue..
const imgs = document.querySelectorAll("img");
let waiting = 0;
let count = 0;
function checkDone() {
if (count === waiting) {
console.log("all images loaded");
}
}
for (const img of imgs) {
if (!img.complete) waiting ++;
else img.addEventListener('load', () => {
count ++;
checkDone();
});
}
checkDone();
<img src="data:image/gif;base64,R0lGODlhEAAQAMQAAORHHOVSKudfOulrSOp3WOyDZu6QdvCchPGolfO0o/XBs/fNwfjZ0frl3/zy7////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAkAABAALAAAAAAQABAAAAVVICSOZGlCQAosJ6mu7fiyZeKqNKToQGDsM8hBADgUXoGAiqhSvp5QAnQKGIgUhwFUYLCVDFCrKUE1lBavAViFIDlTImbKC5Gm2hB0SlBCBMQiB0UjIQA7" style="width:100px;height:100px">
<img src="https://via.placeholder.com/350x150.svg">
<img src="https://via.placeholder.com/300x100.svg">
<img src="https://via.placeholder.com/200x400.svg">

This would be a good case to use requestAnimationFrame.
const printString = // a long string w/ several base64 encoded images;
const printContainer = document.createElement('div');
printContainer.innerHTML = printString;
document.body.appendChild(printContainer);
requestAnimationFrame(function () {
window.print();
});
requestAnimationFrame waits for the next paint and then runs the function, thus you can be sure that window.print() won't run until just after the HTML has been rendered.

Related

JS how to get all img src tags

I want to get the SRC from every <IMG> in twitter.
let n = document.getElementsByTagName("img");
for(tip of n){
console.log(tip.src);
}
and it works for other pages, but it doesn't for twitter. Finally I did this;
let n = document.getElementsByTagName("img");
function example(){
for(tip of n){
if(tip.src == "https://abs-0.twimg.com/emoji/v2/svg/1f1ea-1f1f8.svg"){
tip.src = "https://abs-0.twimg.com/emoji/v2/svg/1f3f3-fe0f-200d-1f308.svg";
}
}
}
setInterval(example, 100);
With setInterval it works, also, twitter loads stuff dynamically so with setInterval I can get all those new results. (How I could do that without using setInterval?)
Also, for some weird reason, the picture doesn't change. It doesn't update.
Update:
function example(){
let n = document.getElementsByTagName("div");
for(tip of n){
console.log(tip.style['background-image']);
if(tip.style['background-image'] == 'url("https://abs-0.twimg.com/emoji/v2/svg/1f1ea-1f1f8.svg")'){
tip.style['background-image']= 'url("https://abs-0.twimg.com/emoji/v2/svg/1f3f3-fe0f-200d-1f308.svg")';
}
}
}
setInterval(example, 10000);
Now it works perfectly, but how could I get the new data without using setInterval eeeeeeevery time and only call to the function when it's necessary?
You have to load image every time you want to iterate them, to get "fresh" img elements
Try this
function example(){
let n = document.getElementsByTagName("img"); // Moved here
for(tip of n){
if(tip.src == "https://abs-0.twimg.com/emoji/v2/svg/1f1ea-1f1f8.svg"){
tip.src = "https://abs-0.twimg.com/emoji/v2/svg/1f3f3-fe0f-200d-1f308.svg";
}
}
}
setInterval(example, 100);
It is not always that the images are shown with img tag.
If you see some cases on twitter, img tag's sibling is an element with img src in its styles. That element is showing the image. Obviously, this is not the case with every single image on twitter.
But for such a case, you change it's background by
let n = document.getElementsByTagName("img");
for(tip of n){
r.previousSibling.style.backgroundImage='YourImage.png'
}
Also, you have to use some kind of interval to get results that are loaded later. Or you could detect DOM changes using Mutation Observer and only fire your function on DOM change.

Prerender images from URL in reactjs

Is it possible to prerender a list of images based on URL to prevent first loading on display with react or JS?
Thanks in advance.
EDIT: I have a carousel of images and all images are display one by one. When an image are are display for the first time this image take some time to be render because she need to be get from the url. And if I display this same image a second time I will not wait. Then I want to know if there is a solution to preload all images directly after get the url in a ComponentDidMount even the loading become longer.
An other example: I have a list of image and I want display them all at the same time. How can I preload them all before end the loading and start the render.
Do you simply want to preload images in advance?
The article of the Preloading Content might help.
componentDidMount() {
const imageUrlList = ['a.jpg', 'b.jpg', 'c.jpg'];
this.preloadImages(imageUrlList);
}
preloadImages(urlList) {
let fragment = document.createDocumentFragment();
for (let i = 0; i < urlList.length; i++) {
const imgUrl = urlList[i];
const linkEl = document.createElement('link');
linkEl.setAttribute('rel', 'preload');
linkEl.setAttribute('href', imgUrl);
linkEl.setAttribute('as', 'image');
fragment.appendChild(linkEl);
}
document.head.appendChild(fragment);
}
Not so clear from what you mean by prerender.
By preloading, do you mean that you want that the component renders only when the image is ready?
If so, you could do something like this:
componentDidMount() {
const img = new Image();
img.src = Newman; // by setting an src, you trigger browser download
img.onload = () => {
// when it finishes loading, update the component state
this.setState({ imageIsReady: true });
}
}
render() {
const { imageIsReady } = this.state;
if (!imageIsReady) {
return <div>Loading image...</div>; // or just return null if you want nothing to be rendered.
} else {
return <img src={Newman} /> // along with your error message here
}
}

Running a javascript animation followed by redirecting

So I'm fiddling with a fun idea for an offline website I'm currently trying to develop.
It'll be offline as it's meant to be practice for later study assignments and such.
My problem is the following: I have a javascript function that replaces a PNG with a GIF and after the animation it should redirect the person to another html I've made.
The source for the JS animation is mainly acquired from here already, but I cannot seem to make it work in the ways I originally intended.
The way it currently works is that I click the png and it'll turn into a gif (no problem there) but what I want is that it runs my js script and then redirect to another .html file (url).
The following is my HTML:
<object id="syringe">
<img src="img/syringe.png" height="750" alt="img/syringe.gif" class="center">
<h2>Click the syringe to inject yourself with a daily dose of puns and jokes</h2>
</object>
The following is the JS:
(function($) {
var getGif = function() {
var gif = [];
$('img').each(function() {
var data = $(this).data('alt');
gif.push(data);
});
return gif;
}
var gif = getGif();
var image = [];
$.each(gif, function(index) {
image[index] = new Image();
image[index].src = gif[index];
});
$('#syringe').on('click', function() {
var $this = $(this),
$index = $this.index(),
$img = $this.children('img'),
$imgSrc = $img.attr('src'),
$imgAlt = $img.attr('data-alt'),
$imgExt = $imgAlt.split('.');
if($imgExt[1] === 'gif') {
$img.attr('src', $img.data('alt')).attr('data-alt', $imgSrc);
} else {
$img.attr('src', $imgAlt).attr('data-alt', $img.data('alt'));
}
$this.toggleClass('play');
});
})(jQuery);
It is to my understanding that I can add a delay and then another function to something in the js - such as the following, which is what I intend to do, however cannot accomplish.
[FUNCTION HERE],1000,
function(){
window.location.href=myredirectionuri;
});
And that is my problem.
It looks like you want to have the gif display for a second or two and then redirect, right? If $this.toggleClass('play'); is the end, then just insert this right after it:
setTimeout(() => window.location.href = myredirectionuri, 1000);
That'll wait for 1000ms and then redirect to the new page.
In the animation script, after it changes to a gif, insert
setTimeout(function() {
/* redirect code */
}, /* length of gif in milliseconds */ )

Return address of image or error if it doesn't exist

I know my problem is the fact that I can't check if the image is good until it has time to load. I'm trying to check width and return after it or the src changes (using onerror to set an src.) I'm always getting stuck with a race condition though, and it errors out long before the height and width or src change. When I reload, the image is cached and it works fine. I don't know how to avoid this. Here is my current code (also not working, it loops until Firefox complains.) I'm setting the return of this function to a variable. There are some writes in there I'm using to see how far it gets, and it stops in the while loop. I tried using getTimeout('tstit = chkload("thisisatest",tinypth);',250);, but that didn't work either. I wish I could force this to load in order...
function buildimg(tehname,myc,pth1,pth2,tinypth)
{
var myimg = pth1+tehname+"/"+tehname+"-"+myc+".jpg";
buildtest("thisisatest",myimg,tinypth);
var testimg = document.getElementById("thisisatest");
var tstit = chkload("thisisatest",tinypth);
while(tstit == false) {
document.write(tstit);
tstit = chkload("thisisatest",tinypth);
}
alert(testimg.src+'-'+testimg.width+'-'+testimg.height);
if((testimage.width > 20) && (testimage.height > 20)) {
return myimg;
}
else if(typeof pth2 == "undefined") {
myimg = "error";
return myimg;
}
else {
myimg = buildimg(tehname,myc,pth2);
return myimg;
}
document.write('No return error in buildimg.');
return "error";
}
/*Builds a hidden img tag for testing images*/
function buildtest(itsid,itssrc,tinypath) {
if(document.getElementById(itsid)) {
var imgobj = document.getElementById(itsid);
imgobj.remove();
}
document.write('<img id="'+itsid+'" style="display: none;" src="'+itssrc+'" onerror="swaponerr(\''+itsid+'\',\''+tinypath+'\')" />');
}
/*Swaps the image to a small picture, so we can detect if it worked*/
function swaponerr(tagid, tinypath) {
var theimg = document.getElementById(tagid);
theimg.onerror = '';
theimg.src = tinypath;
}
/*Recurses to return when the image is loaded*/
function chkload(loadid,tinychk) {
var tehobj = document.getElementById(loadid);
document.write(tehobj.width+'x'+tehobj.height+'x'+tehobj.src);
if((tehobj.naturalWidth > 20) && (tehobj.naturalHeight > 20)) {
return true;
}
if(tehobj.src == tinychk) {
return true;
}
return false;
}
I need to test for an image, and return error if it is non-existent. The code below works fine on my server:
/*Checks if the image at /tehname/tehname-c.jpg exists in either of the paths
outputs it's address if it does, and "error" if not.*/
function buildimg(tehname,index,pth1,pth2)
{
var myimg = pth1+tehname+"/"+tehname+"-"+index+".jpg";
$.ajax({
url:myimg,
type:'HEAD',
error: function()
{
myimg=pth2+tehname+"/"+tehname+"-"+index+".jpg";
$.ajax({
url:myimg,
type:'HEAD',
error: function()
{
myimg="error";
return;
},
});
return;
},
});
return myimg;
}
Unfortunately, I'm trying to do this on a messed up system my work uses. We do have jquery, but the system stores user files on a separate server from code, so ajax won't work. This will eventually be in a .js file, I hope.
Now I've got code starting with:
function buildimg(tehname,myc,pth1,pth2)
{
var myimg = pth1+tehname+"/"+tehname+"-"+myc+".jpg";
var tehimage = new Image();
tehimg.src = myimg;
I tried to have the function load the image, and check its width, but I always get 0, since I can't pre-load the images without knowing how many they are (and I don't want to have some outrageously high number of requests with most being errors.) For some reason (at least on Firefox 4, as that's what my goal is to get working first) tehimage.complete always returns false. I've tried using onerror and onload by a global variable, and a few other methods. I must admit though, I'm not very versed in Javascript, so my callbacks may not have worked.
Please help, I'm getting desperate!
Do you hold the 'power to execute php code' on that user files server? If so, check them in php and output to images.js in that server, than in your html, first load images.js, then your usual javascript.
By the way, jQuery had memory leaks in ajax for ears, don't know about 1.6, but 1.5 definately had those. if you wan't to use ajax to get data from server to javascript - use plain javascript: http://www.w3schools.com/ajax/ajax_xmlhttprequest_send.asp
Edit: Definately worth checking out: http://fettig.net/weblog/2005/11/28/how-to-make-xmlhttprequest-connections-to-another-server-in-your-domain/
Since I'm getting no answers, I'm closing the question. What I ended up doing was a modification to make the image change class in the onload, have the image onerror delete it, and have the rest of the code run in the window onload. Unfortunately, this means the 404s aren't caught before it tries to load more images, so I'm forced to limit the max number of images that can be used (changeable in the function call,) and the images that aren't there just waste a little time.
See the final result here.

Javascript: How can I delay returning a value for img.complete

I've written a script to test for SVG support in the IMG tag:
function SVGinIMG() {
var SVGdata = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNzUiIGhlaWdodD0iMjc1Ij48L3N2Zz4%3D'
var i = document.createElement('img');
i.setAttribute('src',SVGdata);
return i.complete;
}
window.onload = function() {
var hasSVG = SVGinIMG();
alert(hasSVG);
}
This does what I want, except that when I run the script in WebKit browsers the complete property doesn't trigger the first time I load the page; when I refresh the page, it works as it should. The return function is running before the img has finished loading; what's the best method to delay this?
I was complicating things a little; all I really needed was the load event:
function SVGDetect() {
var testImg = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNzUiIGhlaWdodD0iMjc1Ij48L3N2Zz4%3D';
var img = document.createElement('img')
img.setAttribute('src',testImg);
img.addEventListener('load',setCSS,true);
}
This runs another function when the image loads, which is never in the case of browsers that don't support SVG in images.

Categories

Resources