I am converting a site under development to bootstrap. On one of my pages I dynamically create an image based on user input.
I start with a page like this:
And after :
The user clicks on one of the magazine covers and a new image is generated in the upper left corner. Other parts of the image are user choices made on other pages.
My problem is that the javascript generating the code insert width= and height= code in the HTML. Since this defeats bootstraps responsive images, I want to get rid of that but I can't figure out how. Everything else is working fine.
Here is the process:
I start with the HTML:
<div id='FramePreviewDiv'>
<img class='img-responsive' alt='' id='fpS' style='padding:4px' src='' /> </a>
</div>
There is no image on page entry. A default image is generated.
When the customer clicks on one of the magazine covers a request is issued.
getImage('fpS', buildFrameUrl ( 200 ), true);
The function buildFrameUrl returns something like this:
http://www.example.com/BuildFrame.php?mNo=693&tm=C9885&mm=&bm=C9887&noMats=2&size=200&art=siv1n1-0854.jpg&ft=1&matWidth=2.0&maxWidth=200&maxHeight=400&artWidth=8.25&artHeight=11.5&isCropped=0&xStart=0&yStart=0&croppedPxWidth=&croppedPxHeight=&caw=&cah=
Here is the code for getImage
function getImage(pExistingImageID, pImageURL, fShowLoading)
{
var link;
if (fShowLoading)
{
document.getElementById(pExistingImageID).src="/img/loader.gif"; // animated gif of your choice
document.getElementById(pExistingImageID).width=32;
document.getElementById(pExistingImageID).height=32;
}
var img = document.createElement('img');
img.onload = function (evt) {
document.getElementById(pExistingImageID).src=this.src;
document.getElementById(pExistingImageID).width=this.width; // this is the culprit
document.getElementById(pExistingImageID).height=this.height; // this is the culprit
};
img.src = pImageURL;
return false;
}
The HTML generated by this looks like this
<div id="FramePreviewDiv">
<img id="fpS" class="img-responsive" width="200" height="244"
src="http://www.tpfnew.com/BuildFrame.php?mNo=693&tm=C9885&mm=&bm=C9887&noMats=2&size=200&art=siv1n1-0854.jpg&ft=1&matWidth=2.0&maxWidth=200&maxHeight=400&artWidth=8.25&artHeight=11.5&isCropped=0&xStart=0&yStart=0&croppedPxWidth=&croppedPxHeight=&caw=&cah=" style="padding:4px" alt="">
</div>
The width= comes from the value set here:
document.getElementById(pExistingImageID).width=this.width;
Same same for the height.
I tried eliminating the line but that gives me a 32x32 image from the fShowLoading code. If I delete the width and height= from the fShowLoading block, I get the right size image but still with the width= and height= in the resultant html.
Any thoughts on how I can eliminate the generation of the width= and height= html so bootstrap will be able to resize the images responsively?
So what seems to be happening is because the image itself has a height and width assigned to the image its making the image become those sizes. So instead of trying to change the JS to remove the height and width just leave the JS alone and overwrite the properties with CSS like this:
#FramePreviewDiv .img-responsive {
width: 100%;
height: auto;
}
Here is a js.fiddle with an example. Hope that helps
Related
My website uses lazy loading, and I coded a bit of Javascript to keep groups of images at the same aspect ratio, regardless of the user's viewport.
The lazy loading library requires that the src attribute of the images contains a light image like in the example below: a 1 by 1 pixel transparent GIF.
<div class="image-art composition">
<img class="lazyload" src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" data-src="art/game/StanleyParable.jpg" width="264" height="264" />
<img class="lazyload" src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" data-src="art/game/TheBeginnersGuide.jpg" width="461" height="264" />
</div>
Now the problem is that on $(document).ready, this function is called:
function getOriginalImageSizes() {
$(".image-art img").each(function(index) {
originalImageSizes[index] = {
width: $(this).width(),
height: $(this).height()
};
});
}
And for some obscure reason, sometimes my images return a 1x1 size, and sometimes their real size that is specified in their width/height attribute.
The big issue is that then, my rescaling javascript code resizes the images according to this 1-pixel size, which makes it so that then the lazyloading library loads the images into those tiny <img> elements instead of displaying them in their real size.
Does anyone have any idea why $(this).width() returns the width of the content of src instead of the width of the <img> element? According to the documentation here: https://api.jquery.com/width/ it seems like my code should work fine.
As we know, we can serve images from the blobstore using its serving url, and append =sXX to indicate the image's width:
<!-- serving an image of 600px width -->
<img src="{{serving_url}}=s600" alt=""/>
This works well, except we want to serve a smaller image for smaller screens for obvious webperf reasons. How can we serve a different image width depending on screen size?
Thanks for any tips.
You can pull this off easily using javascript, the basic technique is to change the src attribute after evaluating the media queries you're interested in.
For starters, I would avoid filling the src of the image until evaluation, so the full image doesn't load by default; something like this:
<img data-src="{{serving_url}}" height="240" width="320">
Here we rendered the url in a data attribute so we have access to it later in the script. I'm also adding a default size so the space is reserved, but that's optional of course.
Now, we can use Window.matchMedia() to evaluate our query strings:
var mql = window.matchMedia('(max-device-width: 667px)')
if (mql.matches) {
// we're probably running on an iPhone6!
img.src = img.dataset.src + '=s667'
}
The image is fetched using the perfect size!
I'm including a working snippet, it can be easily extended for many query tests for you to try.
var img = document.querySelector('img')
, mql = window.matchMedia('(max-device-width: 667px)')
, size = 1200
if (mql.matches) {
size = 667
}
img.src = img.dataset.src + '=s' + size
img {
object-fit: contain;
}
<img
data-src="http://lh4.ggpht.com/TgjDT-cPRr6bjrpSVQeILk93o4Ouzjo1ygMB6KpmnCHhyH5vJKKRrqxCD2bC3T09CRIP6h5QFsV_l0hnhio5bN7z"
height="240"
width="320"
>
The test image was kindly "provided" by this question. I'm also using object-fit because it makes sense (to me).
I'm using a CSS grid system which is based upon percentages. I have a grid with 4 columns, each 25% of the page's total width. I output my image tags inside of each "25% cell" like this:
<img src="foo.jpg" style="max-width:100%" />
As the browser resizes, the images also resize to fill in 100% of each 25% cell. The browser picks a height, as if I had put "height:auto" (which is implicit when omitted).
Now I want to add lazy loading capability to this. The problem is that before the images are loaded, their height on the page is unknown. The browser has to download the image & observe its aspect ratio, and calculate a height for it. Prior to this, all the images have a height of 1px. Since every image has a height of 1px, they are all considered as "within the viewport" and are immediately loaded.
Currently I have a proof of concept where prior to outputting the img tag, I calculate the images aspect ratio on the server, and output in a data attribute:
<img src="foo.jpg" style="max-width:100%" data-aspect="1.7742" />
Then, upon the event "document ready", I loop through every image and set a fixed 'height' value in pixels prior to lazy loading:
$('img').each(function() {
var img = $(this);
var width = img.width();
var ratio = img.data('aspectratio');
var height = width / ratio;
$(this).css('height', height+'px');
});
This seems to be working, in the sense that it no longer loads all the images at the same time, but only loads images as I scroll.
However, it seems like it could cause new problems, like the images becoming stretched as the user resizes the browser. I would have to switch the 'height' back to 'auto' when a callback fires for lazy loading having completed. That would take care of images the user sees - but the images below the fold would still have an improper 'height' value upon the browser being resized. Every time the browser is resized, I would have to iterate all images that were previously below the fold, measure their updated width, read their aspect ratio, and update the new height, and then retrigger lazy loading to handle anything that is now above the fold. If I don't do this, loading could be triggered too early or too late due to those images having the wrong height value.
My question is, is there any other ways to lazy load images with unknown heights, other than the exact method I've described here, and what ramifications would this have? Is there any downside to my method, other than it being a pain to program?
I had a similar problem recently, combining Isotope with Lazy Load in a responsive layout. Isotope determines the layout based upon the width and height of the images when the page is loaded, so initially, the items were all overlapping because Isotope wasn't calculating the correct size.
To make sure the placeholder items were saving the space, I used the padding-bottom trick you mentioned: http://thisisthat.co.uk/notebook/2013-10-07-lazy-loading-responsive-images (Though it may have not been that exact post.) Here's my markup:
<article class="portfolio-item">
<a class="portfolio-link" href="img/gallery/thumb90.jpg" style="padding-bottom: 66.2%">
<div class="portfolio-image-wrapper">
<img class="portfolio-image" data-original="img/gallery/thumb90.jpg" width="1000" height="662">
</div>
<div class="portfolio-text">
<h1 class="portfolio-item-name">
<span href="#" class="icon" data-icon="e"></span>
<span class="portfolio-item-tags">Bridals</span>
</h1>
</div>
</a>
</article>
That's probably more involved than you need (as the entire .portfolio-text div is an overlay which has quite a bit of styling going on). The real key was just in calculating the bottom padding based upon the width and height of the image (which I did in the template with PHP) and setting that as the padding-bottom of the item that I wanted to save the space.
Here's the more elegant solution, from the comments. It still requires writing the aspect ratio server side, but with a wrapper div:
<div class="lazy"><img src="foo.jpg" style="max-width:100%" data-aspect="0.75" /></div>
Then with JS I give the wrapper div a padding-bottom:
$('div.lazy').livequery(function() {
var c = $(this);
var r = c.data('ar');
c.css('padding-bottom', r * 100 + '%');
});
This gives the div the exact dimensions that the img will eventually consume. I then use the following LESS to load the image within the area the padding consumes:
div.lazy {
max-width:100%;
position:relative;
img {
position:absolute;
width:100%;
height:100%;
}
}
Even cleaner:
Set height and width of images to an arbitrarily-large number (like 2000px x 1000px)
Apply the following CSS to each of the desired images (perhaps via a shared class):
max-width: 100% and height: auto
Smile wide :)
Credit for this approach goes to Github user dryabove, given in this Github issue
if you have images with height and width props (WordPress's default) with loaded 1x1px gif in src - by default in some plugins (looking at you - wp-smush) then just plug this little beast on docready event in your script and it will auto-fix nasty vertical jumping of image when lazy loading ,, I know this is old post, but this is I believe modern js solution:
$('.lazyload').each(function(i,j){
var h = $(j).attr( 'height' );
var w = $(j).attr( 'width' );
var at = `data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 ${w} ${h}'%3E%3C/svg%3E`;
$(j).attr('src', at);
});
Im pretty new to web programming and im working on a site now. In one part of this site, I have collections of 3 pictures. 1 larger one and two smaller thumbnails below it. The goal is to create a way in which i can click on one of the thumbnails and they swap spots with the one large picture. any idea how I would go about doing this? Heres a snippet of code. Thanks!
<div class = 'picture-container'>
<div class = 'large-picture' id = 'lp1'>
<figure style = 'float:left;width:45%;'>
<img src = 'close_table_dupontstudios.png' width = '100%' height = '100%' class = 'no-mobile'>
<figcaption class = 'red-cap'>Our Set-Up</figcaption>
</figure>
<div class = 'picture-content'>
<div class = 'picture-title'>BOUTIQUE PRODUCTION STUDIO</div>
<div class = 'picture-text'>We built a boutique full service production studio that allows for one, two and three person filmed interviews and conversations. We have studio lights, a three camera set-up and remote monitoring. Additionally, our Infinity Wall creates a clean and professional look that allows the film to be about the message.</div>
<!--<div class = 'small-picture'>
<img src = 'hair_and_makeup_dupontstudios.png' width = '175' height = '100'>
</div>
<div class = 'small-picture'>
<img src = 'infinity_wall_dupontstudios.png' width = '175' height = '100'>
</div>-->
</div>
<div class = 'thumbnail-container'>
<figure class = 'thumbnail'>
<img src = 'infinity_wall_dupontstudios.png' width = '100%' height = '100%'>
</figure>
<figure class = 'thumbnail'>
<img src = 'infinity_wall_dupontstudios.png' width = '100%' height = '100%'>
</figure>
</div>
</div>
</div>
There are many ways to solve this problem. The easiest way is to dump all your images (large and small) and only show one at a time.
So in the source code all your large images except the first one will have a class of hidden, which makes them display: none. And then it's just a matter of showing the right large image when the thumbnail is clicked.
To show the right large image you need to associate the thumbnails with the large image by an identifier. Below is an example of setting the href of a thumbnail link to the large image id.
<a href="#lp1">
<figure class="thumbnail">...</figure>
</a>
Now you add the javascript (jQuery).
// preselect all large images
var largeImages = $('figure.large');
// add handler for thumbnail clicks
$('.thumbnail-container').on('click', 'a', function (e) {
e.preventDefault();
var thumbnailLink = $(this),
selectedLarge = $(thumbnailLink.attr('href'));
// hide all the large images
largeImages.addClass('hidden');
// show the large image that corresponds to the clicked thumbnail
selectedLarge .removeClass('hidden');
});
So, the easies way is hide/show, but this means is not the most efficient. It makes the client load all the images even if they are hidden.
A more efficient approach is to add data- attributes in the thumbnails and inside the thumbnail click handler update the large content area with the data from the clicked thumbnail.To "replace" the image you just need to replace src attribute.
So what I have is the following HTML structure
<div class="large">
<a href="http://www.google.com">
<img src="/wp-content/themes/firstone/mythumb.php?src=http://www.example.com/test.jpg&h=600&w=800&zc=1&s=1" width="800" height="600" border="0" /></a>
</div>
Now that mythumb.php is a thumbnailer script that will on the fly create a thumbnail with predefined size limits (in this case 800x600).
However in my CSS file i've set
.large img
{
width:400px;
height:300px;
}
This is done so I achieve a retina effect on iphones/ipad, etc..
However, I was thinking this is better to be accomplished dynamically, and I want to be able to pull the CSS defined width and height and replace those values in the URL
In this case, would it be possible for javascript to change
/wp-content/themes/firstone/mythumb.php?src=http://www.example.com/test.jpg&h=600&w=800&zc=1&s=1
to
/wp-content/themes/firstone/mythumb.php?src=http://www.example.com/test.jpg*&h=300&w=400*&zc=1&s=1
This change should occur before the image loads so we dont waste the users bandwidth downloading the same image twice.
I would also like to only apply this to images being called from the mythumb.php, not my other static images on the page
Does anyone think something like this is possible?
in html layout your img tags, but omit a src attribute
<img id="test" class="variableRes" />
<img id="test1" class="variableRes"/>
in js set up an array of your image paths:
myImages = {
test: "/wp-content/themes/firstone/mythumb.php?src=http://www.example.com/test.jpg",
test1: "/wp-content/themes/firstone/mythumb.php?src=http://www.example.com/test1.jpg"
}
Your css determines the img sizes, so you can query the img style, build the url and assign the src to the img tags
$('.variableRes').each(function(){
var $el = $(this);
var id = $el.attr("id");
var height = $el.css("height").substr(0,-2);//strip "px"
var width = $el.css("width").substr(0,-2);
$el.attr("src", myImages[id] + "&h="+ height +"&w="+ width+"&zc=1&s=1");
})
You can't prevent the loading of the images if they have a src. You can change it to another attr like 'data-src'
<img data-src="/wp-content/themes/firstone/mythumb.php?src=http://www.example.com/test.jpg&h=600&w=800&zc=1&s=1" width="800" height="600" border="0" />
Then you can iterate them like this: (not recommended if you have a lot of imgs though)
var imgs = document.getElementsByTagName('img');
for (var i = 0; i < imgs.length; i++) {
if (notRetina) {
imgs[i].src = imgs[i].getAttribute('data-src')
.replace('h=600', 'h=300')
.replace('w=800', 'h=400');
}
}
You probably can do better than a replace to put the values you want (as in not puting them in the data-src attr).
If you set the src of the image in the HTML source on the server it will in most cases result in the browser fetching the image twice. You can do two things:
Set the src using javascript and initially let it point to a placeholder.
Set the style of the a tag inline.
If the size of the image is different every time I would go for the second option because it is easier and it is faster
SO:
<div style="width: 800px; height: 600px">
<a href="http://www.google.com">
<img src="/wp-content/themes/firstone/mythumb.php src=http://www.example.com/test.jpg&h=600&w=800&zc=1&s=1" width="800" height="600" border="0" /></a>
</div>
The 800 and 600 values would be set using php.
If however the size is always the same in the "large" case you could use the javascript approach you mentioned. It is slightly slower because the CSS that has the width and height definition is fetched asynchronously. You will have to wait for the document.onload event instead of the the domready event to make sure you have the CSS width and height definition. You can then use javascript to fetch the width and height of the div and construct the image src. Because the onload can take a long time (especially is you have external potentially slow content such as social media buttons) the image will not be visible until everything is pulled from the server.
I would create a php large server variable and use that everywhere. It's most definitely the fastest and you still have the size definition on one place.