What's a reliable way in JavaScript to determine the media type (e.g. screen, print, handheld) of the page? I've seen references to document.styleSheets[0].media
, but I've had no luck using this, either because of browser support issues or because I'm not understanding something.
I'm asking because I want something to be hidden by Javascript in screen view, but not in print view. Media-dependent styles can't be relied on to do this because I'm using Prototype to run a toggle switch for the element, and Prototype won't allow an element to be switched to visible if it was declared invisible (display: none
) with non-inline CSS*. I tried to just set media-specific inline styles for the element (<div style="@media print { foo: bar; } @media screen { blargh: bfargle; }">
), but from what I can tell, that's not supported.
I know that I can cycle through the stylesheets and check to see if a print-specific linked stylesheet is active or not, but I'm currently in a situation where various media-specific style declarations are all mixed around in a single linked stylesheet, so that's no good. And yeah, I can just split up the stylesheets into different media types, but I'd first like to figure out whether or not I can just reliably pull the media type out of the DOM with JavaScript, completely independently of CSS.
I've tried that trick of "hide an element for the print view, then check to see if it's visible with JavaScript" but that's always resulted in (when I load up print preview) JavaScript determining that the supposed-to-be-hidden elements are visible and performing whatever DOM manipulation I tell it to, despite the fact that those elements aren't visible. If anybody would like more details about what I'm talking about here, I can elaborate in an edit.
*This is something that I haven't understood and am constantly irritated by.
[..], Prototype won't allow an element to be switched to visible if it was declared invisible (
display: none
) with non-inline CSS. This is something that I haven't understood and am constantly irritated by.
You probably already seen this but the documentation for e.g. show
(there are other related functions with the same note) states that:
Element.show
cannot display elements hidden via CSS stylesheets. Note that this is not a Prototype limitation but a consequence of how the CSSdisplay
property works.
So, it's a known problem and they blame CSS. However, consider the following document (I haven't used Prototype before, so I'm not sure if this is the recommended way to wait for the DOM to load, but it seems to work):
<!doctype html>
<script src="prototype.js"></script>
<script>
document.observe("dom:loaded", function() {
$("victim").show();
});
</script>
<style>
p#victim {display:none;}
</style>
<p id="victim">Hello world!
As you already know, this will not work. Why? Well, how would Prototype know what to "reset" the display
property to when you tell p#victim
to show
itself? (In other words: how can it find out what should have been the value of display
if the display: none
wasn't present in the ruleset with the p#victim
selector.) The answer is simple: it can't. (Think about it for a second. What if another ruleset would modify the value of display
if the display: none
wasn't present in our ruleset with the p#victim
selector? (I.e. we can't assume that e.g. p
always should be set to block
, other rulesets may have changed that value.) We can't remove the display
property from a ruleset in an style sheet, and we can't remove the entire connection between the element and the ruleset because it may contain other properties and so on (even if it would be possible it would be, imho, non-obvious to find which ruleset to do this with). Of course, we could go on and on with this, but afaik there is no known solution to this problem.)
Then why does the inline alternative work? First, lets look at how show
is implemented:
show: function(element) {
element = $(element);
element.style.display = ''; // <-- the important line
return element;
}
Afaict the only important thing this function does is to set element.style.display
to an empty string (''
), which "removes" display
from style
. Great! But what does that mean? Why would we want to remove it?! First we have to find out what element.style
actually represents and modifies when we modifies its values.
The MDC documentation for element.style
states that:
Returns an object that represents the element's
style
attribute.
Note the last word: attribute. element.style
≠ the current "calculated" style for the element, it's just an list of the properties in the style
attribute (see MDC for a longer/better explanation).
Consider the following document:
<!doctype html>
<script src="prototype.js"></script>
<script>
document.observe("dom:loaded", function() {
$("victim").show();
});
</script>
<p id="victim" style="display:none;">Hello world!
style="display:none;"
hides p#victim
but when the DOM finish loading Prototype will change it to style=""
, and the browser will recalculate the value for the display
property (the value from the browser's default style sheet in this case).
But, consider the following document:
<!doctype html>
<script src="jquery.js"></script>
<script>
$(document).ready(function(){
$("#victim").show();
});
</script>
<style>
p#victim {display:none;}
</style>
<p id="victim">Hello world!
jQuery handles the style sheets stuff correctly, in this case anyway! This isn't as simple to explain as the Prototype solution and there are to many layers of awesomeness for me to read through right now, and there are many cases where jQuery fails to calculate the correct value for display
. (Quick last note: Firebug..., but I guess it uses some Firefox exclusive stuff or something.)