I'm developing a printing tool using HTML5 canvas. By open a new window and write the page canvas as image in new window document then finally print the window document.As a test, I've tried to print the pages(greater than 100 pages) in chrome latest version (Version 46.0.2490.71) it does not print the whole page. In chrome print preview window display only partial page like if we print the 110 pages document means it display only 24 or 38 pages(it display the page randomly). But the whole pages are added in the newly created window for printing.
I used the below code for printing the pages.
var _printWindow = window.open('');
_printWindow.document.write('<html><BODY>');
_printWindow.document.write('<center>');
for (var i = 1; i <= _totalPages; i++) {
var canvas = this._printpages(i);
_printWindow.document.write('<img src="' + canvas.toDataURL() + '"style="border:1px solid;height:"' + _pageHeight + '"px;margin:0px"><br />');
}
_printWindow.document.write('</center></body></html>');
_printWindow.document.close();
_printWindow.print();
You are calling print() directly after you have finished writing the <img> tags. However, it takes time for all those images to load (asynchronously). Thus, by the time you call print(), the images have not yet finished loading and some may not have had their height determined yet, resulting in an unexpected number of pages.
To fix this, you should call print() only after the onload event has fired on all of the image elements. Here is how I solved it:
var nImages = _totalPages;
function imageLoaded() {
if (--nImages === 0) {
_printWindow.print();
}
}
// ...snip...
// replace your `for` loop with the following:
for (var i = 1; i <= _totalPages; i++) {
var img = new Image;
img.addEventListener('load', imageLoaded);
img.src = /* get the data URL from the canvas */;
// here, add in your style properties
_printWindow.document.body.appendChild(img);
}
Related
I know this is going to be difficult to get help for, but anyway:
In short: this page renders OK the first time on Safari (both Mac and iPhone/iPad), the second (after refresh) time some things are not shown. Opening the page in Private mode -> works always. Opening it in Chrome -> works always
In long: the page is a temporary solution and hacked together... Currently it is driven by Caspio (a no-code rapid development environment). The no-code comes at a price however of a limited possibilities. We are rewriting the system in a proper front-end/back-end environment but for the time-being we should get this page to work.
The page consists of 2 blocks rendered by Caspio. I need to get some elements from block 2 and put them into an element of block 1 (and hide block 2 then) with JS. Again the options on how to get the elements are limited by what Caspio is providing, it's very dirty code which I hope to get rid of asap!
<script type="text/javascript">
window.addEventListener("load", function (e) {
const dataPageId = "46629000bb2da6866c8b4cc09dc1";
// Default image for the promotion (in case no image uploaded)
var theImage = document.createElement("img");
theImage.setAttribute("src", "images/noImageFound.png");
theImage.setAttribute("alt", "No Image");
// get the text form the placeholder virtual fields and hide it (not possible in Caspio to hide it)
// First get the title
var promoTitleVirtual = document.querySelectorAll("[id*='cbParamVirtual1']");
var promoTitleParagraph = document.createElement("h3");
var promoTitle = document.createTextNode(promoTitleVirtual[0].value);
promoTitleParagraph.appendChild(promoTitle);
promoTitleVirtual[0].style.display = "none";
// Now the description
var promoDescriptionVirtual = document.querySelectorAll("[id*='cbParamVirtual2']");
var promoDescriptionParagraph = document.createElement("span");
promoDescriptionParagraph.classList.add("w-screen")
var promoDescription = document.createTextNode(promoDescriptionVirtual[0].value);
promoDescriptionParagraph.appendChild(promoDescription);
promoDescriptionVirtual[0].style.display = "none";
// The Image
var images = document.getElementsByTagName("img");
for (i = 0; i < images.length; i++) {
if (images[i].src.includes(dataPageId)) {
theImage = images[i];
}
}
var promotionImage = document.getElementById("promotionImage");
// reposition the radio so it looks better
var promoAnswers = document.querySelectorAll(
"[class*='cbFormBlock24']"
);
promotionImage.appendChild(promoTitleParagraph);
promotionImage.appendChild(theImage);
promotionImage.appendChild(promoDescriptionParagraph);
theImage.parentNode.lastChild.style.width = theImage.width + "px";
promotionImage.appendChild(promoAnswers[0]);
});```
The image is always shown, the Title and the Description only the first time
Found the problem... can be interesting for those using the Caspio environment!
Apparently Caspio doesn't load all the data immediately so even though I wait until everything is loaded :
window.addEventListener("load", function (e)
it is not...
Hacky (but this code is it anyway, can't wait until we have this re-written) I added a :
setTimeout(function() {
...
}, 300)
to my code so it waits until Caspio is done with its business. (200 was ok, just put 300 to be on the safe side)
I am loading images from remote server.
how do i make sure that all images are loaded in correct order
also how do i make sure the alert is called only when all images are loaded
the below code does not load the images in correct order and calls the alert before the images are loaded.
$(window).load(function () {
$('.fancybox').each(function () {
var hh = $(this).attr('href');
var img = new Image();
img.src = hh;
function TIMG(i) {
if (img.complete != null && img.complete == true) {
$('.mag').append('<img src="' + hh + '" class="cls" />');
} else {
setTimeout(TIMG, 1000);
}
}
setTimeout(TIMG, 1000);
});
alert(hi);
});
Based on the new information that you just want to add them in order as soon as they are ready, you can do that like this
$(document).ready(function () {
// preload all the images as fast as possible
var imgs = [];
$('.fancybox').each(function () {
// get all images preloading immediately
// so there is the best chance they are available when needed
var img = new Image();
imgs.push(img);
img.onload = function() {
addReadyImages();
}
img.className = "cls";
img.src = $(this).attr('href');
});
function addReadyImages() {
// show all images that are ready up to here
while (imgs.length && imgs[0].complete === true) {
// append the loaded image from the start of the array
$('.mag').first().append(imgs[0]);
// remove the one we just did from the start of the array
imgs.shift();
}
}
});
Working demo: http://jsfiddle.net/jfriend00/sCHws/
This algorithm works as follows:
Start preloading all the images as soon as possible.
Store the image objects in an array in the order that the .fancybox items are encountered in your page
Set an onload handler for each image so we know immediately when it's ready
In the onload handler for any image, append all images at the front of the array that are ready and then remove them from the array
P.S. I've assumed there is only one .mag item and thus we don't need to make separate copies of the image objects (much more efficient that way - rather than creating new image objects). If that is not the case, then please disclose your HTML so we can see the whole problem.
Since browsers will normally not block when downloading images,the only way to ensure that the images are loaded in the right order would be to load them serially...using a queue and then only beginning the next after the previous has completed. That is definitely not recommended though since that could easily create a severe performance hit.
Waiting for when all of the images are loaded would likely best be done by the .promise() and related methods in jQuery: http://api.jquery.com/promise/, and watching for when the images are load()ed. If you particularly want to control the display of images (which is more likely what you'd be concerned with), then they could be displayed in sequence after they are loaded.
The following Code Works in every browser accept every version of IE, can someone please help! There seems to be a problem with the cat.onload method....I have done so many tests and am at a loss...any help would be appreciated
Thanks!
var bannerCount = 0;
var bannerImgsArr = new Array();
bannerImgsArr[0] ='image1.jpg';
bannerImgsArr[1] ='image2.jpg';
bannerImgsArr[2] ='image3.jpg';
bannerImgsArr[3] ='image4.jpg';
$(document).ready(function() {
preloadBannerImages();
});
function preloadBannerImages(){
//create new image object
var cat = new Image();
//give Image src attribute the image file name
cat.src =bannerImgsArr[bannerCount];
//if the counter represents the last image in the current gallery enter here:
if(bannerCount == 3){
//When the image has been loaded the following function is exicuted
cat.onload = function() {
//Removes the LOADING message hard coded into the html page;
document.getElementById("bigbox").removeChild(document.getElementById("loading"));
}
}else{
//when current image is finished loading increments the img index and
//calls the loading function again for the next image in the gallery
bannerCount++;
cat.onload =preloadBannerImages;
}
}
You are defining the onload handler for the image after setting the src property. Do the one before the other.
If that doesn't solve your problem, you really need to elaborate what exactly doesn't work.
Don't rely on the onload-event of images, it may not fire if the image was loaded before and comes from the cache
[edit]
The function most likely ends and calls itself again before the image is downloaded, so the image isn't cached. Load the images into a persistent store (e.g. array) that hangs around until they are all downlaoded and cached.
[/edit]
You don't have to wait for the DOM to be ready to load images:
var bannerCount = 0;
var bannerImgsArr = ['image1.jpg','image2.jpg','image3.jpg','image4.jpg'];
var imageArray = [];
for (var i=0, iLen=bannerImgsArr.length; i<iLen; i++) {
imageArray[i] = new Image();
imageArray[i].src = bannerImgsArr[i];
}
Now when the DOM is ready, you can use one of the preloaded images.
...
cat.src = imageArr[bannerCount].src;
...
I have an array of two urls that I read and feed into an iframe src value using:
document.getElementById("iframe").src = unescape(dashboard.url);
Immediately after this line, I issue an alert(document.getElementById("iframe").src), which all works fine.
My problem is, is that the first URL in the array seems to load correctly and the alert display the correct URL being used by the iframe.
But when the second URL comes around to be processed and fed into the iframe src value, the alert displays the correct URL, but my iframe which is on the same page is not being refreshed with the correct URL and so still displays the first URL in my array that worked previously.
Am I missing something - is this an IE6 bug of some sort as from what I can see, the iframe second time around is not being called.
I noticed this by putting an onLoad="alert('Test');" in my iframe and it only appeared once and NOT the second time for the second URL.
Any ideas?
By the way, I am using a setTimeout() call to cycle through the arrays.
Thanks.
See it in action. (works x-browser)
<iframe id="rotator" src="http://...first"></iframe>
<script>
// start when the page is loaded
window.onload = function() {
var urls = [
"http://...first",
"http://...second",
// ....
"http://...tenth" // no ,!!
];
var index = 1;
var el = document.getElementById("rotator");
setTimeout(function rotate() {
if ( index === urls.length ) {
index = 0;
}
el.src = urls[index];
index = index + 1;
// continue rotating iframes
setTimeout(rotate, 5000);
}, 5000); // 5000ms = 5s
};
</script>
I'm using the following code to insert some HTML into a div, and to preload any images that might be contained in that HTML (the html var's data is actually fetched from an AJAX request in the real code). This is to prevent the browser from loading the fetched HTML's images upon showing the div (using the slideDown event) - because this results in the effect's fluidity being broken as it loads image mid-transition. I suppose I could use an interlaced JPEG so that the dimensions of the image are known almost immediately, but obviously it'd be nice to get a cleaner method worked out. :P
var html = '<img src="images/test.jpg" alt="test" />';
$('div.content').hide().html(html);
$('div.content img').each(function(){
var img = new Image();
img.src = $(this).attr('src');
$(this).attr('src', img.src);
});
$('div.content').slideDown('normal');
I'm using the Image object and its subsequent assigning as per the advice given here, but unfortunately the image still isn't cached by the browser using this method, because the sildeDown() effect is still interrupted as the image loads.
Any help or alternative methods? Many thanks.
Edit - 21st Sept 09
Progress! Turns out the browser was caching the image, I just wasn't giving it time to do so (it just needed a second to load with an alert() or setInterval()). Now introducing what is probably the messiest code ever - I am using an infinite loop to create that pause.
The new method extends the old code above by binding a function (that adds each image's src to an array) to that image's successful load event. It then gets stuck in an infinite loop as it waits until all the images have loaded and therefore appeared in the array. This seems to work as a way to synchronously pre-load images - but a problem remains; the while() loop for some reason cycles infinitely even once all the images are loaded, unless I add an alert() to pause it for a moment.
The new code:
var html = '<img src="images/test.jpg" alt="test" />';
$('div.content').hide().html(html);
// define usr variables object
$.usrvar = {};
// array of loaded images' urls
$.usrvar.images = [];
// boolean for whether this content has images (and if we should check they are all loaded later)
$.usrvar.hasimages = false;
// foreach of any images inside the content
$('div.content img').each(function(){
// if we're here then this content has images..
$.usrvar.hasimages = true;
// set this image's src to a var
var src = $(this).attr('src');
// add this image to our images array once it has finished loading
$(this).load(function(){
$.usrvar.images.push(src);
});
// create a new image
var img = new Image();
// set our new image's src
img.src = src;
});
// avoid this code if we don't have images in the content
if ($.usrvar.hasimages != false) {
// no images are yet loaded
$.usrvar.imagesloaded = false;
// repeatedly cycle (while() loop) through all images in content (each() loop)
while ($.usrvar.imagesloaded != true) {
$('div.content img').each(function(){
// get this loop's image src
var src = $(this).attr('src');
// if this src is in our images array, it must have finished loading
if ($.usrvar.images.indexOf(src) != -1) {
// set imagesloaded to trueai
$.usrvar.imagesloaded = true;
} else {
// without the pause caused by this alert(), this loop becomes infinite?!
alert('pause');
// this image is not yet loaded, so set var to false to initiate another loop
// (ignores whether imagesloaded has been set to true by another image, because ALL
// need to be loaded
$.usrvar.imagesloaded = false;
}
});
}
}
$('div.content').slideDown('normal');
I made the following solution but it hasn't been tested, so you're warned ;)
// HTML (any formatting possible)
// no src for the images: it is provided in alt which is of the form url::actualAlt
var html = "<p><img alt='images/test.jpg::test' /><br />Some Text<br /><img alt='images/test2.jpg::test2' /></p>";
$(document).ready(function() {
// Reference to the content div (faster)
var divContent = $("div.content");
// Hide the div, put the HTML
divContent.hide().html(html);
// Webkit browsers sometimes do not parse immediately
// The setTimeout(function,1) gives them time to do so
setTimeout(function() {
// Get the images
var images = $("img",divContent);
// Find the number of images for synchronization purpose
var counter = images.length;
// Synchronizer
// will show the div when all images have been loaded
function imageLoaded() {
if (--counter<=0) $('div.content').slideDown('normal');
}
// Loading loop
// For each image in divContent
$.each(images,function() {
// Get the url & alt info from the alt attribute
var tmp = $(this).attr("alt").split("::");
// Set the alt attribute to its actual value
$(this).attr("alt",tmp[1]);
// Wire the onload & onerror handlers
this.onload = this.onerror = imageLoaded;
// Set the image src
this.src = tmp[0];
});
},1);
});
Create an interval/timeout and let it check your compterGenerated css-height, if it's autosized it'll begin from 0 and end to 100 (for example). But in Safari it loads the height before the image, so it'll propably not work in all browsers...
I was playing with this and I created a slightly different solution. Instead of pushing images onto an array when they are loaded, you push them all onto an array in the loop, then in the load event you remove them from the array and call a 'finished' function. It checks if the images array is empty, and if it is then it clears up and shows the content.
var html = '< animg src="images/test.jpg" alt="test" />'; // not allowed to post images...
$('div.content').hide().html(html);
// preload images
// define usr variables object
$.usrvar = {};
// array of loaded images' urls
$.usrvar.images = [];
// initially no images
$.usrvar.hasimages = false;
$('div.content img').each(function() {
// if we're here then this content has images..
$.usrvar.hasimages = true;
// set this image's src to a var
var src = this.src;
// add this image to our images array
$.usrvar.images.push(src);
// callback when image has finished loading
$(this).load(function(){
var index = $.usrvar.images.indexOf(src);
$.usrvar.images.splice(index,1);
finish_loading();
});
// create a new image
var img = new Image();
// set our new image's src
img.src = src;
});
if(!$.usrvar.hasimages) finish_loading();
function finish_loading() {
if($.usrvar.hasimages) {
if($.usrvar.images.length > 0) return;
}
$('div.content').slideDown('normal');
}
Edit: Looking at Julien's post, his method is better. My method works in a similar way but like the original solution keeps track of images by an array of srcs rather than just a count (which is more efficient).
Edit 2: well I thought it was a better solution, but it seems it doesnt work for me. Maybe something to do with the load event getting called too close to each other. Sometimes it will work but sometimes it will hang when loading images, and the image counter never reaches zero. I've gone back to the method in my post above.
Edit 3: It appears it was the setTimeout that was causing the problem.
This is what I use. As you can see by my points, I'm no pro, but I found this somewhere and it works great for me and seems much simpler than everything posted. Maybe I missed a requirement though. :)
var myImgs = ['images/nav/img1.png', 'images/nav/img2.png', 'images/nav/img3.png', 'images/nav/img4.png', 'images/expand.png', 'images/collapse.png'];
function preload(imgs) {
var img;
for (var i = 0, len = imgs.length; i < len; ++i) {
img = new Image();
img.src = imgs[i];
}
}
preload(myImgs);