I'm working on a script uses jQuery's data() function and HTML5 data attributes to dynamically switch an image's src attribute based on a media query. The idea behind this is to serve a lo-fi image by default (optimized for mobile), and serve a hi-fi image for larger screens. (This is not necessarily limited to images.) The script works 100% in Chrome/Opera/Safari/iOS but not completely in FF and IE.
<img src="ex1_lo-res.png" data-websrc="ex2_hi-res.png" alt="example">
<img src="ex2_lo-res.png" data-websrc="ex2_hi-res.png" alt="example">
A live example of this in use is responsetheme.com, where above 480px wide, the image should be pink, and below 480px wide, it should be yellow. I know that both data() and Modernizr.mq are supported in FF and IE—I tested them without the resize() function. So I think the issue has something to do with the trigger or the each() or resize() function. Any idea on what I'm missing?
jQuery(document).ready(function($) {
/* get elements that have a data-websrc attribute */
$websrc = $('[data-websrc]');
$websrc.each(function() {
/*
Set data-osrc equal to element's original src value.
This allows us the ability to access the original src
(even after we replace the attribute).
*/
var $this = $(this);
$this.data('osrc', $this.attr('src'));
});
$(window).resize(function() {
/*
Check breakpoint.
(Modernizr.mq checks the media query and returns boolean.)
*/
airsrcWEB = Modernizr.mq('screen and (min-width:480px)');
/*
Replace src with data-websrc (if above breakpoint).
Otherwise fallback to data-osrc (original src).
*/
$websrc.each(function() {
var $this = $(this);
var src = ( window.airsrcWEB ) ? $this.data('websrc') : $this.data('osrc');
$this.attr('src', src);
});
}).resize(); // trigger resize handlers
});
Also, I wasn't sure as to whether I have the functions in the most efficient way as far as which was inside which, so I'd also like to hear any tips for speeding this up. =)
Update 1: I also tried with the ternary like this and still the same issue:
var src = ( airsrcWEB ) ? $this.data('websrc') : $this.data('osrc');
Update 2: I figured out the issue with FF. Apparently a FF6 window won't resize below about 556px wide. I tested the script with a breakpoint above that and the switch worked. (Even the examples on mediaqueri.es won't shrink below 556px wide in FF6.)
You already found out that FF has a minimal window size. I don't know the exact value, but I believe it's a percentage of the initially available viewport width.
This is a restriction of XUL, the language in which FF was written.
The question is, is this really a problem in your case? The only persons that fiddle around with the window size are (front-end) webdevelopers. Normal users just load a page and stick with it, so basically I'm thinking that you might not really need to attach this functionality to a resize event.
Furthermore, this is only an issue when users are shrinking the window, not when expanding. If they already loaded the hi-res image, why bother loading the low-res aswell?
Related
Note that I'm not asking how to make a div the size of the "window" or "viewport" for which there are plenty of existing questions.
I have a web page of some height and width, and I'd like to add an empty, top-level div (i.e., not one containing the rest of the page) with a size exactly equal to the page's height and width. In practice, I also want it to be at least the size of the viewport.
I know I can do a one-time calculation of the height and width in JavaScript:
var height = Math.max(document.body.scrollHeight,
document.documentElement.clientHeight);
var width = Math.max(document.body.scrollWidth,
document.documentElement.clientWidth);
But this value can change based on images loading, or AJAX, or whatever other dynamic stuff is going on in the page. I'd like some way of locking the size of the div at the full page size so it resizes dynamically and on-demand.
I have tried something like the following:
function resetFakeBg() {
// Need to reset the fake background to notice if the page shrank.
fakeBg.style.height = 0;
fakeBg.style.width = 0;
// Get the full page size.
var pageHeight = Math.max(document.body.scrollHeight,
document.documentElement.clientHeight);
var pageWidth = Math.max(document.body.scrollWidth,
document.documentElement.clientWidth);
// Reset the fake background to the full page size.
fakeBg.style.height = pageHeight + 'px';
fakeBg.style.width = pageWidth + 'px';
}
// Create the fake background element.
fakeBg = setFakeBgStyle(document.createElement('div'));
document.body.appendChild(fakeBg);
// Keep resizing the fake background every second.
size_checker_interval = setInterval(resetFakeBg, 1000);
Limitations
This is for a Chrome extension, and I'd like to limit my modification of the page to adding this single div. This means that adding CSS to modify the height and width of the html and/or body tags is undesirable because it might have side-effects on the way the rest of the page is rendered.
In addition, I do not want to wrap the existing page in the div because that has the potential to break some websites. Imagine, for example, a site styled with the CSS selector body > div. I'd like my extension to break as few websites as possible.
WHY OH WHY WOULD I NEED TO DO THIS?
Because some people like to hold their answers hostage until they're satisfied that I have a Really Good Reason™ for wanting to do this:
This is for an accessibility-focused Chrome extension that applies a CSS filter across an entire page. Recent versions of Chrome (>= 45) do not apply CSS filters to backgrounds specified on the <html> or <body> tag. As a result, I have chosen to work around this limitation by copying the page's background onto a div with a very negative z-index value, so that it can be affected by the page-wide CSS filter. For this strategy to work, the div needs to exactly imitate the way the page background would appear to a user—by being the exact size of the document (and no larger) and at least filling the viewport.
setInterval() is your best friend in cases like this where you want the .height() and .width() of an element to be asynchronously specified all the time to something that can be dynamicly altered by user input and DOM tree changes. It is what I dub as a "page sniffer", and arguably, works better than $(document).ready if you are working in multiple languages (PHP, XML, JavaScript).
Working Example
You should get away with setting the width and height in the window resize function, you might wanna add it in a load function as well, when all data/images are loaded.
just add width=100%
e.g;-
Hello World
I think you must do it like this:
...
<body>
<script>
function height()
{var height = Math.max(document.body.scrollHeight,
document.documentElement.clientHeight);}
function width()
{var width = Math.max(document.body.scrollWidth,
document.documentElement.clientWidth);}
</script>
<div height="height()" width="width()">
</div>
</body>
...
My dev site uses lots of Skrollr animation at 1024px resolutions and up. Under 1024px, I don't want the animation to show, so I hid all of the images and whatnot.
However, the javascript that gets called to make the animation work is still getting called on smaller resolutions and causing some issues.
Is there a way to basically say "If the resolution is less than 1024px, ignore these JS files"?
I tried putting them in a DIV and using my existing CSS media queries to "display: none" the DIV on smaller resolutions, but that doesn't work.
FYI, these are the files being called:
<script type="text/javascript" src="/js/skrollr.min.js"></script>
<script type="text/javascript" src="/js/homepageanimation.js"></script>
On top of the jQuery(function($) { in http://workwave.joomlatest01.mms-dev.com//js/homepageanimation.js put something like
jQuery(function($) {
if(screen.width < 1024) {
return;
}
// skrollr stuff....
}
so all the skrollr functions won't be called on screen sizes with a width below 1024px.
The easiest way is too use jQuery..
$(window).width();
plain Javascript:
var w = window.innerWidth;
var ow = window.outerWidth; //toolbars and status, etc...
if(w > 1024) {
//Skrollr
}
from there an small if to trigger the Skrollr event
I would suggest conditionally loading the script. Basically the script only gets loaded if the screen size is greater than 1024.
if(window.innerWidth >= 1024){
var file = document.createElement('script')
file.setAttribute("type","text/javascript")
file.setAttribute("src", "/js/skrollr.min.js")
}
A nice approach here would be to only call the function that initiates the Skrollr functionality at given screen sizes. A real quick Google suggests that Skrollr has a .init() function that gets things rolling.
Without seeing how the JS is set up it's hard to give any solid advice, but here's an idea:
You have a JS file for the page/site that contains a conditional that checks the width of the window before initializing the plugin after the document is ready.
$(document).ready(function() {
if ($(window).width() > 1023) {
skrollr.init();
}
});
jQuery makes this a lot easier too, so it's worth taking advantage of that.
Another option to consider instead of going via window width (which can sometimes be inconsistent with the CSS widths among different browsers) is to test against a CSS rule and whether it is true, so use one you know would be true at a size above 1024px, and this would eliminate any inconsistency.
Within this condition link the JQuery files as demonstrated in other answers.
I am a skilled database / application programmer for the PC. I am also an ignorant html / javascript / web programmer.
I am creating some documentation about some .Net assemblies for our intranet. Ideally I would like to display an image full size if the browser window can fit it. If not then I would like to reduce it and toggle between a small version and full size version by a click. It is a dependency chart and can be different sizes for different pages. I would prefer a single function to handle this but being it is for our use none of the requirements I mentioned is set in stone. I would like to make it work well but nothing is mandatory.
I read a lot of stuff but couldn't find anything that matched what I wanted. First I tried this (after a few iterations):
<img src='Dependancy Charts/RotairAORFQ.png' width='100%' onclick='this.src="Dependancy Charts/RotairAORFQ.png";this.width=this.naturalWidth;this.height=this.naturalHeight;' ondblclick='this.src="Dependancy Charts/RotairAORFQ.png";this.width="100%";'>
It has problems. First off it enlarges a small image and it looks funny. Second I would have to put the code in every page. Third it requires a double click to restore it. I was going to live with those short commings but the double click fails. I can't figure out how to restore it.
So I tried to get fancy. I couldn't figure out how to get past problem 1, but solved 2 and 3 by creating a function in a separate file. Then I ran into what appeared to be the same problem. This was my second attempt:
function ImageToggle(Image)
{
if (ImageToggle.FullSize == 'undefined')
ImageToggle.FullSize = false;
if (ImageToggle.FullSize)
{
Image.width='100%';
ImageToggle.FullSize = false;
}
else
{
Image.width=Image.naturalWidth;
ImageToggle.FullSize = true;
}
return 0
}
And in my page:
<img src='Dependancy Charts/RotairAORFQ.png' width='100%' onclick='ImageToggle(this)'>
Can what I want be done? It doesn't sound impossible. If it is a large amount of effort would be required then alternate suggestions are acceptable.
You're probably interested in the max-width: 100% CSS property, rather than a flat-out width:100%. If you have a tiny image, it'll stay tiny. If you have a huge image, it gets resized to the width of the containing element.
For example: http://jsbin.com/kabepo/1/edit uses a small and a huge image, both with max-width:100%. As you can see, the small image is untouched, the huge image is resized to something sensible.
I would recommend that you set a the max-width: 100% CSS property for the image.
This will prevent the image's width from expanding to be greater than the container's width.
You can also do the same with max-height: 100% if you are having problems with the image overflowing vertically.
Please see this JSFiddle for an example.
(Note: If you set both a width and a height attribute on the <img> tag directly or in your CSS file your image will not be scaled proportionally.)
Does it have to be a toggle or would a mouseover work for you as well?
<style>
.FullSize { width:100px; height:auto; }
.FullSize:hover { width:90%; height:auto; }
</style>
<img src="Dependancy Charts/RotairAORFQ.png" class="FullSize">
Note: when image is made larger IN the page - the surrounding content will be displaced around it - depending on how you have set up the layout.
Also if you have any body margins or table or div paddings, using image width at 100% will make the page scroll. To check just change 90% to 100% and work your way up / down.
You could also force the image to be a specific size until the browser gets made smaller by the user / has a smaller resolution.
<style>
.FullSize {width:1000px;max-width:100%;height:auto;}
</style>
<img src="Dependancy Charts/RotairAORFQ.png" class="FullSize">
A tip: the image used must be the largest one. So minimum width of lets say 1200 pixels wide (if that is the forced image size you use). That way regardless of size it is it will remain clearer than a small image becoming a large. Since it's an intranet, file size shouldn't be an issue.
Thanks all for your help. Rob and Mike both pointed me to an excellent solution. I now have my page load with an image that fits the browser window, resizes with the browser and if the user is interested they can expand the image and scrollbars appear if necessary. I got this to work in a function so minimal code is needed for each page.
To load the image:
<p style="overflow:auto;">
<img src='Dependancy Charts/RotairAORFQ.png' width="100%" onclick='ImageToggle(this)'>
</p>
And the function:
function ImageToggle(Image)
{
if (ImageToggle.FullSize == 'undefined')
ImageToggle.FullSize = false;
if (ImageToggle.FullSize)
{
Image.style="max-width: 100%";
ImageToggle.FullSize = false;
}
else
{
Image.style="max-width: none";
Image.width=Image.naturalWidth;
ImageToggle.FullSize = true;
}
return 1
}
if you want to get current browser window size and if you want to do it on a click event so try this in jquery or javascript:
<script>
$("#myButton").click(function(){
var x = window.innerHeight; // put current window size in x (ie. 400)
});
</script>
I have function that is supposed to wait till background image is loaded and then add image's ratio to it's container and fade background image container in. It seems to work in Chrome, but fails in Internet explorer 8 and 9, and not always works in 10. In ie8 it doesn't seem to understand jquery on('load') event. In ie 9 and 10 it not always finds width of background image. How can I make it work in ie8+ and other major browsers?
js:
$('.item').each(function(index) {
var thisItem = $(this);
var image_url = thisItem.find('.img').css('background-image');
var img = new Image();
img.src = image_url.replace(/(^url\()|(\)$|[\"\'])/ig, '');
if (image_url){
//initialize only when background img is loaded
var $img = $('<img>').attr('src', img.src).on('load', function(){
//don't allow img to be larger than it is
if(img.width < thisItem.width()){thisItem.css({'max-width':img.width+'px'});}
var ratio = img.height/img.width;
thisItem.find('.item_ratio').css({'padding-bottom':ratio*100+'%'});
thisItem.find('.img').fadeIn(800);
});
}
});
html:
<div class="item">
<div class="img" style="background-image:url('some_url'); width: 100%; background-size: cover;"></div>
<div class="item_ratio"></div>
<!-- Some other not important html ->
</div>
The load event on images has been shown to be unreliable/absent, which is why there's a well-known jQuery plugin to help smoothen things out cross-browser. I'd recommend having a look at that instead rather than trying to figure out the browser issues and edge cases like missing load events when browsers fetch images from cache etc.
Edit: code sample to make it work with a dangling <img/> (untested)
//initialize only when background img is loaded
var $img = $('<img>').attr('src', img.src).prependTo(thisItem).hide().imagesLoaded(function() {
//don't allow img to be larger than it is
//... [your code]
});
I used this approach here. (The thumbs on the left are a single background image sprite, I animate those in once the image has loaded).
As an aside, you may also be able to use background-size: contain in CSS. Doing that might be more performant in newer browsers (if images are large) and much simpler since you don't need any code. It's unsupported in IE8 though, which you can test if you're using Modernizr. That would probably be my preferred approach, but I normally include Modernizr anyway. YMMV.
When using javascript to swap images the HTML is updated fine but what Opera actually displays is not unless you scroll or resize the window. A picture of what happens when you scroll explains it best.
alt text http://img340.imageshack.us/img340/9455/87855188.png
Any ideas?
EDIT: The source of the problem seems to be that the image is inside a div that has float right.
EDIT2: This http://trac.dojotoolkit.org/ticket/3158 would suggest that it's a bug that was fixed and is back again.
Odd, I've never experienced problems like that before. I think that is a combination between browser and the graphics card / GUI, I've had exactly this behaviour before but in all sorts of applications (OpenOffice), not only the browser.
Ideas on how to maybe trick it into updating:
Set opacity to .99 and then back to 1
Change position by 1px (jerky though)
Set display to none and to block again (flickers, not nice, but to see whether it works)
Move it off the screen for a (milli)second and back again (probably flickers)
I have faced the same problem. This seems to be a bug related with Presto based Opera versions (< 12.5). The src attribute of the img elements seems to be updating correctly but the changes are not reflected to DOM. Triggering reflows are sadly not working. Only detaching and reattaching the node seems to fix the problem. I have tried following that led to no avail:
Change src to null, and then to new value,
Change src to null, change position (top/left etc), change width/height,
Trigger above with delay (i.e. 100ms delay between null and new value)
Performing various combination of above with any order.
The only way that correctly fixed the problem was detaching related node from DOM and reinserting. Here is the piece of code if anyone needs:
var isOperaPresto = this.navigator.userAgent.includes("Opera") && this.navigator.userAgent.includes("Presto");
if(isOperaPresto)
{
/* if browser is opera presto, updating image elements' sources will not upload the DOM visual.
So we need to do some hacking. Only thing that works is to remove and reAppend the relevant node... */
Object.defineProperty(HTMLImageElement.prototype, "src", {
enumerable: true,
configurable: true,
get: function() {
return this.getAttribute("src");
},
set: function(newSrc)
{
/*max-size confinement is required for presto if parent is display flex. Image will go out of its available size otherwise*/
this.style.maxHeight = this.style.height;
this.style.maxWidth = this.style.width;
this.setAttribute("src", newSrc);
/*we have to put this node back to exactly where we rip it from*/
var parent = this.parentNode;
if(this.nextElementSibling != null)
{
var reference = this.nextElementSibling;
parent.removeChild(this);
reference.insertAdjacentElement("beforebegin", this);
}
else
{
parent.removeChild(this);
parent.appendChild(this);
}
}
});
}