views:

1064

answers:

5

I looked around and didn't see this question asked yet.

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. Oh, and 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 detemining 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. Anyone that can provide any insight into it gets a big upvote from me.

A: 
document.getElementsByTagName('link').each(function( link )
{
  //do something with link.media
  //hide whatever you need to hide, etc
});
geowa4
Thanks, but not what I was looking for.
Phantom Watson
+1  A: 

EDIT: Sorry (Note to self: RTFQ Rob, sheesh!). A quick googling turfed this up and a jquery plugin if that's any use. It's basically using the @media rules but not inline as far as I can tell so your browser support may vary but it's the best you can hope for I think :(

sanchothefat
I described doing something similar that didn't work. For whatever reason, under print view in Firefox, an element that was hidden by the print-specific styles was determined by Javascript to actually be visible, rendering this strategy inexplicably useless.
Phantom Watson
A: 

See if this works... I think you can do this for every type and just check if the statement returns null or equivalent, then apply your rules.

From this site: http://www.webdeveloper.com/forum/showthread.php?t=82453

IE:

if (document.styleSheets[0].media == 'screen'){...}

FF/NS:

document.getElementsByTagName ('LINK')[0].getAttribute ('media')

or

document.styleSheets[0].media.mediaText

and a script to check it in all browsers (except for some new ones, so be careful :))

http://student.agh.edu.pl/~marcinw/cssmedia/

this script works in a if-statement as a boolean true/false! Hope you'll get it to work!

xaddict
A good solution indeed, but it unfortunately violates the "independent of CSS" parameter of the question. Though I might end up using it anyway!
Phantom Watson
+1  A: 

[..], 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. Anyone that can provide any insight into it gets a big upvote from me.

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 CSS display 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.)

cic
I'm having no luck getting an answer to my main question, but I'm very impressed with your explanation here. Many thanks, "cic".
Phantom Watson
A: 

Hello,

I've tested my script mentioned above - it now works with all new browsers, incl. IE8 final.

The script moved here: http://cssmedia.pemor.pl/ Feel free to use it if only you want.

With regards Marcin Wiazowski

Good job with the script, but I'm still waiting to hear whether or not a css-independent solution exists for this problem.
Phantom Watson