Responsive background images - javascript

Currently I'm working on building a solution for having responsive background images set via JavaScript. I am using Picturefill to create cross-browser support.
What do I need? There is an element on my page which needs a pretty little background photo. On the page there is a picture element which loads conditionally images. However I specified one default image source if the browser doesn't have proper picture element support.
With MooTools I check what photo is supposed to show up on domready, on window resize I check my image 'src' again to replace my current picture if needed (with a bigger or smaller one).
In FireFox the picture element replaces the image 'src' in the DOM, it works perfect!
Chrome (and IE) don't replace the image 'src' so my photo will always have the default photo dimensions according to MooTools. However, when you hover over the image 'src' via the Chrome developer tools it does show the correct image source for that media query.
I can get in way too many details but trust me I really need to do it like mentioned above, who want to help me with this? :)
<script>
var Placeholder = $('Placeholder');
var CoverImage = $('MyCover').getElement('img').src;
Placeholder.setStyle('background-image', 'url(' + CoverImage + ')');
var $timer = null;
window.addEvent('resize', function(){
var ResponsiveImage = function(){
var $ResponsiveImage = $('MyCover').getElement('img').src;
$('Placeholder').setStyle('background-image', 'url(' + $ResponsiveImage + ')');
};
clearTimeout($timer);
$timer = ResponsiveImage.delay(250, this);
});
</script>
<picture>
<source media="all and (max-width: 30em)" srcset="1.jpg">
<source media="all and (min-width: 30.063em) and (max-width: 48em)" srcset="2.jpg">
<source media="all and (min-width: 48.063em) and (max-width: 80em)" srcset="3.jpg">
<source media="all and (min-width: 80.063em)" srcset="4.jpg">
<img src="2.jpg">
</picture>
Thanks for the possible solutions, I am very grateful!
Cheers,
Stefan

Perhaps I don't understand exactly what you are trying to do, but instead of trying to use the script to control the dimensions why not use the CSS property background-size: cover; instead? That is what I always use for responsive background images not that it is widely supported by browsers.

You should really look at http://foundation.zurb.com/docs/components/interchange.html.
Maybe you can get the script and adapt it to your case?

First of all your markup:
In picture land you don't need to use min-width and max-width. The first source, that matches is the media query is used:
<picture><source media="(max-width: 30em)" srcset="1.jpg">
<source media="(max-width: 48em)" srcset="2.jpg">
<source media="(max-width: 80em)" srcset="3.jpg">
<source srcset="4.jpg">
<img src="2.jpg"></picture>
Now, what you need is actually the property currentSrc, but be aware it needs to be handled different than the src property, because it is updated, after the image is loaded (so don't use resize, use load).
Fortunately there is already a simple lazySizes plugin called bgset, that does exactly what you need. Here you find a demo and here you find the documentation. Of course you can also check out the source code and build your own.
In case you want to use the plugin your markup changes to something like this:
<div class="lazyload" data-bgset="1.jpg [(max-width: 30em)] | 2.jpg [(max-width: 48em)] | 3.jpg [(max-width: 80em)] | 4.jpg"></div>

I am using the currentSrc property to get the updated image source and it works like a charm! Thanks everyone for the help!

Related

Avoid an excessive DOM size when lazy loading images

I have a problem with dom size (3,044 elements).
I have a slider (Slick Slider) and images used in it causing DOM size prolem.
Img tag has lazy loading, it loads background image first and then loading main image data-src.
Reference to DOM size is this image :
<img src="/assets/img/img_bg_md.png" data-src="/uploads/images/202212/image_430x256_639c5b0a29f1f.webp" alt="Title" class="lazyload img-responsive img-post" width="1" height="1">
Is there a way to solve that problem ?
I'm not sure I understand what you want, but I'll try to help. You could set the already fixed image size to the image size you already know it will be. So you could put a background image of the image for it to load prettier
<style>
.img-responsive {
background-color: #ccc;
}
</style>
<img
src="/assets/img/img_bg_md.png"
data-src="/uploads/images/202212/image_430x256_639c5b0a29f1f.webp"
alt="Title"
class="lazyload img-responsive img-post"
style="width: 1px;height: 1px"
/ >
<script>
const imagens = document.querySelectorAll('.img-responsive');
var src, resp;
for (const imagem of imagens) {
src = imagem.getAttribute("data-src");
resp = src.match(/image_(\d+)x(\d+)_/);
if(const && resp.length > 2){
imagem.style.width = resp[1] + 'px';
imagem.style.height = resp[2] + 'px';
}
imagem.onload = function() {
imagem.parentElement.style.backgroundColor = 'transparent';
imagem.style.width = imagem.naturalWidth + 'px';
imagem.style.height = imagem.naturalHeight + 'px';
}
}
</script>
Other suggestions:
There are a few things you can try to reduce the DOM size when using lazy loading for images:
Use a lighter version of the main image as the background image. This can help reduce the size of the DOM element and also improve the loading speed of the page.
Use a placeholder image as the background image. This can help reduce the size of the DOM element and also improve the loading speed of the page.
Use a smaller image as the background image. This can help reduce the size of the DOM element and also improve the loading speed of the page.
Use responsive images. By using the srcset attribute, you can specify different sizes of the same image for different screen sizes, which can help reduce the size of the DOM element and also improve the loading speed of the page.
Use image optimization tools. There are several tools available that can help you optimize your images, which can reduce the size of the DOM element and improve the loading speed of the page.
Use a content delivery network (CDN). By using a CDN, you can offload the delivery of your images to a network of servers around the world, which can help reduce the size of the DOM element and improve the loading speed of the page.
It's worth noting that there is no one-size-fits-all solution to this problem, and you may need to try a combination of these approaches to find the best solution for your specific use case.

how to avoid changes on video height while changing its attributes

click on a button changes video poster and src attributes
after the click video height becomes 0 - a short period - but enough to produce an ugly effect on entire page
how to avoid this?
note - lorem.mp4 and ipsum.mp4 have the same resolution and dimensions
<video class='vtop' id='player' controls poster='lorem.jpg'>
<source src='lorem.mp4' type='video/mp4'>
</video>
js
var player = $('#player');
$('button').on('click', function(){
player.attr('poster', 'ipsum.jpg');
player.attr('src', 'ipsum.mp4');
player[0].play();
});
I tried your code and add a css file to it and give the video a fix height:
video {
height: 400px;
}
I think it is enough and I don't see any special effect when I click button.
The height is changed because your video player height depends on the video height. And when you change src video player height changes as well. What you need to do, is just specify height for your video tag.

How to preload images with multiple srcset with javascript or jQuery?

I'm trying to build a script to preload images with jQuery, for a small application I'm working on.
I've read different tutorials and right now I managed to have it working like this:
var imageList = ['img1.png', 'img2.png', 'img3.png'];
$.each(imageList, function (index, imageName) {
var $img = $('<img>')[0];
$img.onload = function () {
console.log('loaded');
};
$img.src = imgPath + imageName;
}
This works fine. I load the images from an array I prepare, I then create all the img tags and then append them in the DOM where needed.
I'm wondering now, though, how can I do something similar if I have images with multiple srcset.
Let's say I have 3 sizes for each image, but they could be more, normally I would put something like this in the html:
<img srcset="large.jpg 1024w,
medium.jpg 640w,
small.jpg 320w"
sizes="(min-width: 36em) 33.3vw, 100vw"
src="small.jpg">
Now, how shall I apply the preloading to this?
1) I could preload all the sizes for each image in Javascript, but this would be pointless, because the whole purpose of having multiple srcset is to load just one
2) I could put the img tag in the DOM, let the browser choose the only size needed and load from Javascript.
The problem with the second option is that the browser is loading the images from the DOM, so why loading them again in Javascript? It's possible that I am completely wrong about this and maybe I'm missing something. What's the correct way to do it?
You can use the same idea that you had in your script, but instead set the sizes and srcset attributes.
const image = new Image()
image.onload = () => console.log('loaded')
image.sizes = '(min-width: 36em) 33.3vw, 100vw'
// This will trigger the browser to start loading the appropriate picture
image.srcset = `
large.jpg 1024w,
medium.jpg 640w,
small.jpg 320w
`
Can't you detect the browser width in Javascript, and use that to load the proper images? You could have one array for small images, one for medium, and one for large.
Even better, if you'd name the images such that the size is a suffix (image1_small.png and image1_large.png) you'd only need one array, and just append the correct suffix.
Alternatively, keep separate directories, small/ large/ etc and just give the proper path, according to window width.

Avoid downloading images on mobile

I'm working on a responsive website where all viewport versions share the same HTML/CSS. The problem is that I don't need all images from the desktop version displayed on the mobile version. I hide those elements with display: none in my CSS.
Nevertheless the browser downloads those images because the HTML gets parsed before CSS layout happens. But I just want to download the images which I need for the mobile version so I can reduce HTTP requests and the overall download size.
A solution for me was to use the <picture> element like this:
<picture>
<source srcset="image.jpg" media="(min-width: 992px)">
<img src="" alt="an image">
</picture>
The image will not download when the browser window width < 992px because I let the fallback source attribute empty src="". Unfortunately Safari doesn't support it yet. Therefore I don't want to use it.
Now I came up with my own JavaScript/JQuery stuff but I'm not sure if it's a good idea.
HTML:
<img src="" alt="img" data-device="mobile" data-src="img-mobile.jpg">
<img src="" alt="img" data-device="tablet" data-src="img-tablet.jpg">
<img src="" alt="img" data-device="desktop" data-src="img-dektop.jpg">
JavaScript:
function loadImages() {
var mobile = window.innerWidth < 768;
var tablet = window.innerWidth >= 768 && window.innerWidth < 992;
var desktop = window.innerWidth >= 992;
if(mobile) {
$('[data-device]').each(function() {
if($(this).attr('data-device') === 'mobile') {
$(this).attr('src', $(this).attr('data-src'));
}
}); // end each
} else if(tablet) {
$('[data-device]').each(function() {
if($(this).attr('data-device') === 'tablet') {
$(this).attr('src', $(this).attr('data-src'));
}
}); // end each
} else if(desktop) {
$('[data-device]').each(function() {
if($(this).attr('data-device') === 'desktop') {
$(this).attr('src', $(this).attr('data-src'));
}
}); // end each
}
}
$(document).ready(function() {
loadImages();
});
$(window).resize(function() {
loadImages();
});
The browser parses the HTML with empty src attributes. After the page finished loading, loadImages() will be called.
For example, if the browser width is < 768px. The function iterates through all elements with a data-device attribute. It will check the data-device value and if it's mobile it will take the value from the data-src attribute and puts it as a value to the actual image src attribute.
This means that the browser will just download and display the images for the mobile version of my site.
However, I don't like this. It looks like hacked together to get around this problem somehow. I mean there must be another solution to this because I'm sure that everybody who works with responsive sites will soon or later run into the same issue. I have googled this problem a lot but I haven't found a satisfying solution yet. Everybody is saying something different.
How you guys are tackling this problem? What is the best practice?
I must say that your idea to workaround the issue looks good.
Just another idea, how about replacing the img element for a div element and load a background-image using the #media styles in css for each device? That will load one or another image depending on which device is used, and I believe that is something lots of developers use these days.
Hope that helps!

Prevent large image flickering on src change

I've a problem with image flickering with large images.
In my body i have 5 images:
<img id="img1" src="img1.png" width="250">
<img id="img2" src="img2.png" width="250">
<img id="img3" src="img3.png" width="250">
<img id="img4" src="img4.png" width="250">
<img id="img5" src="img5.png" width="250">
and one I'm dragging one of them with jQuery UI, all are changing their src and on dragend as well:
function dragStart() {
$('#img2').attr('src','newimg2.png');
$('#img3').attr('src','newimg3.png');
$('#img4').attr('src','newimg4.png');
$('#img5').attr('src','newimg5.png'); }
so fine so good. But I need to use large images (2000 x 2000px) because all images can be clicked and then they will animate to the full size of the viewport that they dont pixelate.
$this.animate(
{ width: 1800, top: -650, left: -250 },
{
duration: 4000,
easing: 'easeOutElastic'
})
I think because of the size of every image, they are flickering. Does anyone of you have an idea how to prevent this flickering on images, if all src change at the same time ?
Thanks for your effort
The problem you described does not sound like a pre-loading issue to me.
For preloading would happen, when you load ANOTHER image from the server once you start to move it around. But like I have read your Question you are moving the DOM-object containing your image in SRC around.
Thats most likely a Browser issue, because he has to scale your images down from 2k x 2k to lets say 100 x 100. That is some expensive interpolation stuff to do there.
So your main problem could be, like you mentioned, the size of the image.
Even preloading would not be of use, because you would have the same issues then.
In my eyes you should have two versions of your image: One small one (the size you want to drag around) and a big one, the one you want to display.
The big one can either be loaded automatically in background or on demand, when a user clicks on an image.
In the web it is quite common, to show scale the small image to screen size with smooth animations and start to preload in the background and when the preload finished, replace the fullscreen image to remove the pixel effect.
I hope I made myself clear.
The key to what you are trying to do is called preloading. However, you'll need to think carefully about how you want to do this.
Preloading involves loading the image in an img tag off-screen, but still in the DOM. This caches the image locally, which means that the next time you attempt to use the same source, it'll pull from cache instead of querying the server for the image (and, thus, flicker).
Preloading an image is a simple matter:
(new Image()).src="mysource.png";
What you want to decide is when you want to load the images. IF you load them all at first, you'll potentially use up a lot of bandwidth. If you load them on-click, you'll get buffering.
You can check if an image is loaded using the onload event present on img tags and wrapped within jQuery if needed, as follows:
var i = new Image();
i.onload = function() {
console.log("Loaded");
}
i.src = "mysource.png";
Credits to Robin Leboeuf for the concise Image() form.
You can use a function like this to preload your images:
function imagesPreload(){
var imgArray = new Array("path/to/img1.jpg", "path/to/img2.jpg", "path/to/img3.jpg");
for (var i=0; i<imgArray.length; i++) {
(new Image()).src = imgArray[i];
}
}
See the comments. You should ensure that the images are loaded before you show them. This is called pre-loading and can e.g. be achieved by having hidden images (not using display:none but placing them offscreen) that have the SRC that you want.
Edit: see the more elaborate answer by #Sebástien !

Categories

Resources