JavaScript: how to change image source without issuing two HTTP requests - javascript

Here's the code:
<div id="wrapper">
<img src="lowres/image123.jpg">
</div>
PREMISSES:
The <img> element is generated by a proprietary system backend and uses a low resolution image as source. I can only operate on it using pure javascript (no jquery!).
I NEED TO change de src attribute to a high resolution version located in a external server, eg.: src="//cdn.provider.com/highres/image123.png" (images have the same name but different locations).
THE PROBLEM: doing it after <img> insertion into DOM issues 2 (two) HTTP requests, one for the lowres image and other for the highres - and I have lots of images on the page!
In order to FIX IT, I was wondering if it is possible to manipulate <img> just before its insertion into DOM to change src appropriately, for example by intercepting an <img> event "beforeInsertion" or an event "afterInsertion" of the <div> wrapping it.
Cheers!
UPDATE 1: to make things clear: 1) I don't have accesss to the backend/server side; 2) I don't want to display the low resolution image, just the high resolution; 3) I dont' know the file name in advance, I need to get it from the <img> and append it to the path of the high resolution version stored in the CDN (both images have the same name); and 4) I can do it with the code bellow, but at the cost of TWO HTTP requests, which is what I want to avoid and what has motivated this question! ;)
var img = document.getElementById("wrapper").childNodes[0];
img.src = getHighResolutionImagePath(img.src);

You have to change url before adding <img> tag to document (e.g in server side script). Browser will get image from given url as soon as it'll receive tag, so it's not possible to replace it then. Especially that js will execute after getting all elements used on website.

When you're developing an app for retina displays and you want to let the browser know it's a high resolution image, you would append "#2x" to the picture name, like so:
background-image: url('../img/logo2#2x.png');
Try that and see if the browser adopts the same policy.
Source: http://benfrain.com/how-to-serve-high-resolution-website-images-for-retina-displays-new-ipadiphone4/

You must listen to mutation events, specifically DOMNodeInserted
Full documentation is available at MDN > Mutation Events
Once you have detected that an image as been added (you must filter the proper image type) you can replace the source for the one you want.
A small snippet taken from MDN:
element.addEventListener("DOMNodeInserted", function (ev) {
// ...
}, false);
Beware that performance will be greatly degraded

Related

Save multiple <img> with all css applied elements from self DOM to jpg/png file

I developed a project on Laravel and Vue Js. In a part of this site, I have made an App in Vue whom allow the user to drags a <img> tag on another <img> tag, both contained in a DIV.
There are many (and the most unsupported) CSS applied to both images, as filters, 3dtransforation, mask, background-source...It works smooth on both Chrome and Firefox.
What I have to do now, is to save in jpg/png the "result" of all the trasformations applied to those two images, or to say it better, the html of the div container have to be converted in a image file and saved on server/rendered on the page after a click.
I didn't realise this will have been the hardest part.. Whatever I tried until now didn't worked. I tried using
Domtoimage -> The problem that I have with this javascript library is that I'm loading 1 image from the server (local currently) and 1 image from an S3disk on the AWS, and there are cross-browser compatibility problems.
Browsershot -> The issue experienced for me here is again about having the files on different storages, but here the problem is on the localhost. Hww, doing a try with both image on S3, some of the CSS properties are not rendered (mask, opacity, transform)
HTML2CANVAS -> Again both issues: cross origin, and not rendering most of the required CSSs.
At this point I really not longer know where to look out for a solution. It seems that for as easy is to set the css for two elements that works, it's impossible to ask java or the server to just "print out" what you see, exactly as you are seeing it.
About "printing": I noticed (trying to find a workaround) that even if by pressing print page, on the preview, some CSS are missing, while they are clearly visible in the page.
I'll be definitively grateful if somebody have any suggestion
As I said in comments, here are some solutions. About the solution that involves wkhtmltoimage in server side, is as follow:
wkhtmltopdf and wkhtmltoimage are open source (LGPLv3) command line
tools to render HTML into PDF and various image formats using the Qt
WebKit rendering engine
Download and install wkhtmltopdf wich includes wkhtmltoimage
Send from client all what you need (more detail below)
In backend create a html file with all what you need, you could include css and javascript
run wkhtmltoimage http://yourHtml.html yourdesiredImage.jpg
You have your page with all rendered like a browser in yourdesiredImage.jpg
As you have to send to backend some behaviors that users perform with css,
you could use window.getComputedStyle(element) to store all properties applied to an element into an object.
You have to send the object created by window.getComputedStyle(element) to the backend with ajax and reapply the rules.
To reapply those styles you could use something like:
var stylesComputed = {}//My object from frontend
var source = document.getElementById("source");
var target = document.getElementById("target");
var copy = document.getElementById("copy");
copy.addEventListener("click", function(){
var styles = "";
for(let i = 0; i<stylesComputed.length-1; i++){
styles += stylesComputed[i]+":"+stylesComputed[stylesComputed[i]]+"; ";
}
target.setAttribute("style", styles);
document.getElementById("t2").innerHTML = "Target (copied)";
});
full example here: https://jsfiddle.net/5c9rhxbn/

Detecting when an image has loaded

Is it possible to detect - using jQuery or pure DOM manipulation / events - when an image that was injected into the document has fully loaded? For example, a script needs to replace one image with another. If it just changes the src attribute, the user will see an image disappear briefly (or not so briefly, depending on their connection). So, a better option would be to create a new image element in some hidden place (or, if cross-platformly possible, out of DOM entirely) and switch images only when the new one is ready. Can this be done?
EDIT: It might be a separate question, but still... Is there also a way to detect when an image has failed to load?
You can use image.onload = function(){<...>};
Image is the actual image you want to get the load of.
<img src='...' id='image'>
In order to check if the image finished loading do the following->
document.getElementById('image').onload = function(){<...>}
or
document.getElementById('image').addEventListener('load',function(){<...>},false);
If you want to check if the image failed to load do this->
document.getElementById('image').onerror = function(){<...>}
or
document.getElementById('image').addEventListener('error',function(){<...>},false);
Note
.addEventListener method won't work in IE8. If you are planning on supporting it I can edit my answer.
yes you can do that by using the .load() event.
like,
$('#img1').load(function(){ alert('loaded'); });
the alert will be displayed when the resources for that particular selector has loaded fully.
The .load() event does not always work as expected and is prone to fail under different circumstances. For broadest browser support I suggest to use DeSandro's imagesLoaded (which I find to be best practice anyway): https://github.com/desandro/imagesloaded

block image network requests with javascript

I'm looking for a reliable way to block image network requests with javascript. I read this: Prevent images from loading
It seems that it's a bit hit-and-miss as to whether the image request or the javascript will fire first.
Does anybody understand the order in which stuff happens? Can I tie into some sort of hook where I can run some javascript before any network requests are made? Or at least abort any existing ones.
Thanks.
Images are loaded when the src attribute is set. You can either dynamically create the image tags, or just set the src attribute when you want the image to load.
Also note that different browsers render pages in different ways. so preventing the load with javascript, is likely to work on certain browsers and not others
You can't avoid that - your browser will always try to load the images of your img tags. You will have to load the images dynamically via JavaScript.

Get HTML elements from a document in the server and show them dynamically in the client

Context
I am making an application for showing a synchronized HTML5 slideshow to about 50 spectators in a wireless LAN with no internet access.
I run a Node.js server in one of the computers and connect with the 50 clients via Socket.IO (Btw, only one of them controlls the presentation).
The hardware is a domestic wireless 802.11b/g router and 50 mobile devices (tablets, netbooks, smartphones).
Problem
When the slideshow starts, it takes too long (about 10 minutes or more for a 5 MB slideshow) for the clients to see it, since the router has to send the complete slideshow to all the clients at the same time.
How my slideshow looks
<html>
<head>
<title>My Slideshow</title>
<script src="javascripts/slidesplayer.js"></script>
<link rel="stylesheet" href="/stylesheets/style.css">
</head>
<body>
<div id="slides-containter">
<div class="slide" id="slide_1">
<!--Contents such as images, text, video and audio sources -->
</div>
<div class="slide" id="slide_2">
<!--Contents -->
</div>
<!--A bunch of slides here-->
</div>
<script>
// Here I load the slides
</script>
</body>
</html>
What I would like to do
At the beginning, I would like to load the slides-container element completely empty.
Then, as I advance through the slideshow, I'd like to GET from the server the div representing the next slide, and append it to the DOM so that only when that is done, the client starts to download the pictures and othet stuff only for that slide (thus, decreasing significantly my network overload).
Another relevant fact is that the slideshow (including the slidesplayer.js) is automatically generated from an external software that parses PowerPoint presentations to this HTML5 format and that we will use a lot of presentations that are already made in PowerPoint.
My first impression is that I should accomplish this by using jQuery-ajax, but I don't know exactly how to do it the good way, since my idea is just copying the div.slide elements in separate files.
Update: This answer suggests using jQuery for DOM manipulation before displaying. It seems that jQuery requests the resources everytime you manipulate a DOM object, even if it is not inserted into your current DOM. So, one possible solution would be working only with strings. You can see more about this issue in this and this questions.
One solution would be to treat this as a front-end solution. The front-end should arguably only eat as much as it can take at any one time.
I'm assuming it's external resources (imagery etc) as opposed to the slideshow markup itself that's making up the most of those 5MB, in which case the DOM should not attempt to call those resources until they are necessary.
I would suggest serving the whole slide document to an ajax call but only introducing the markup to each slide as it is called. Something like this:
$.ajax('path/to/slides', {
async: false,
complete: function ajaxCallback(slidesDOM){
// Pull out the individual slides from your slideshow HTML
$slides = $(slidesDOM).find('.slide');
// For each of these...
$slides.each(function prepareSlide(){
// Store a reference to the slide's contents
var $slideContent = $($(this).html());
// Empty the contents and keep only the slide element itself
var $slideWrapper = $(this).empty();
$slideWrapper
// Put the slide where you want it
.appendTo('.slidesContainer')
// And attach some kind of event to it
// (depending on how your slideware works, you might want to bind this elsewhere)
.on('focus', function injectContent(){
// Put the content in — NOW external resources will load
$slideWrapper.append($slideContent);
// Unbind this function trigger
$slideWrapper.off('focus', injectContent);
});
})
}
});
1) You shouldn't be streaming payloads with SocketIO. Socket is made for low-load. If you need to transmit en-masse, I'd recommend using a standard HTTP AJAX request. Then, you can use Socket.IO to control which slide you are on.
2) Try AngularJS. They've basically done all the thinking for you regarding view switching (which is essentially what you are doing). They have a great tutorial, which helps alot.
3) To simplify you Socket calls, I'd recommend using ConversationJS both client and server side.
As I said in the question, manipulating DOM elements will cause the browser to download the resources, even if you don't insert the elements that use that resources in your DOM.
In my case, the best solution I could make was to use some sort of lazy loading at least for the img tags (but it could be easily extended for other tags, such as audio and video).
What I did was replacing replacing the src attribute with another name (xsrc in this case) and adding a custom empty src attribute to all img tags.
<img id="someImg" src="#" xsrc="foo.png"></img>
Then, with jQuery I changed the src attribute value to that of xsrc whenever I needed to dowload the image.
// When I want the image to be downloaded from the server
$('#someImg').attr( 'src' , $('#someImg').attr('xsrc') )
You can see more about the idea behind this in the questions I already mentioned (this and this).

jQuery changing <img> src with $(selector).prop takes a long time

So there's no issue with this code functionality itself. I have something like this:
<div>
<div><img id="imageToChange" src="/path/image.png" /></div>
<div id="textToChange">Text</div>
</div>
I have another part of my code, that changes the image src/text with jQuery.
function changeImage() {
$('#imageToChange').prop('src', '/path/image2.png');
$('#textToChange').html('New Text');
}
As you may expect, this works exactly as I expect it to. But with 1 quirk.
In all the main browsers (chrome/FF/IE). The image takes a long time to change.
So for example, when I call changeImage(), the text will change instantly, but the image may not change until 1-2 seconds later. (Not large images by any stretch, about ~6KB, and local)
I haven't found anyone else really complaining about it, but what I'm wondering is if there's any way to speed up the changing of the image src? Perhaps a better way to do it?
This is jquery 1.8.0 as well.
Thanks
I have seen this behavior before. The delay is caused by the image not being cached and the subsequent load time. The only solutions I know of:
Preload your images with JavaScript Image objects.
Handle the load event on the image and update the text after the image as loaded. Note jQuery lists some issues to watch out for:
Caveats of the load event when used with images
A common challenge developers attempt to solve using the .load()
shortcut is to execute a function when an image (or collection of
images) have completely loaded. There are several known caveats with
this that should be noted. These are:
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
http://api.jquery.com/load-event/
You may want to try altering the attribute using the jquery .attr function. If I recall correctly the src tag of an image is an attribute not a property. Although both .prop and .attr do relatively the same function, to ensure consistent behavior between browsers you may want to use the .attr tag instead.
$('#imageToChange').attr('src', '/path/image2.png');
As far as the delay goes, this could be due to the size of the image. The browser has to make a GET request to the server for the image and then paint the DOM with it. If the image is large, it could cause a time lapse between when the code changes the source and when the new image is properly written to the DOM. If the image is large, you may want to consider scaling it down or optimizing it for web use.
Hope this helps.
You can pre-load the image using the Javascript Image object.
In the head of your document put
<script type="text/javascript">
img2 = new Image();
img2.src = "/path/image2.png";
</script>
when you change the src of the image you fetch another image file. it makes an HTTP request for the new image, so it needs to load before showing it. could this be it?
btw, for this reason you can pre-load the image with js. either add
<img src="path/to/image.jpg" style="display: none" />
to your html or using JS
var im = new Image(1,1);
im.src = "path/to/image.jpg";
this way the image will be cached
It's the delay of network. Try this:
<div>
<div><img id="imageToChange" src="/path/image.png" /></div>
<div id="textToChange">Text</div>
</div>
<img src='/path/image2.png' style='display:none'>
it is likely the load time of your images. if this is the case, loading the image the first time should be the only slow one. a follow up load, after changing the image to something else, would be fast.
$('#imageToChange').prop('src', '/path/image1.png');
//slow, need to fetch image
$('#imageToChange').prop('src', '/path/image2.png');
//slow, need to fetch image
$('#imageToChange').prop('src', '/path/image1.png');
//fast, it already has this image
As a solution, you could try preloading your images. Or, better yet, use css sprites.

Categories