原文链接:http://www.nczonline.net/blog/2012/01/19/css-media-queries-in-javascript-part-2/
本文由小豪翻译
在我的前一篇文章中,我介绍了CSS media query在JavaScript中的应用,包括一个自制的实现函数和CSSOM Views里的matchMedia()方法。在CSS和JavaScript中,Media query都是那么实用,以至于我继续对其进行了一些研究,探索更好的利用方式。最后我发现,matchMedia()方法还有一些我在写第一篇文章时没有意识到的有趣特性。
再次调用matchMedia()会返回一个MediaQueryList对象,这个对象可以让你确定给出的media类型是否和浏览器当前状态匹配。MediaQuery对象的matches属性会使用一个布尔型数据来表明匹配的结果。很明显,在每一次调用marches属性的时候,它都会获取一次浏览器的状态:
var mql = window.matchMedia("screen and (max-width:600px)");
console.log(mql.matches);
//resize the browser
console.log(mql.matches); //requeries
这显然非常实用,因为这样你就可以保持对MediaQueryList对象的引用,从而可以重复检查query与页面之间的状态。
Chrome和safari还有一个怪异的行为。即使matches属性的初始值是正确的,默认情况下还是不会更新matches的值,除非页面含有一个对应相同的query和至少一个规则定义的media块。比如说,为了让一个表现形式为”“screen and (max-width:600px)““的MediaQueryList对象正确显示出来(包括能正确触发事件),你必须在你的CSS中包含一些内容:
@media screen and (max-width:600px) {
.foo { }
}
media块中必须含有至少一个CSS规则,但是该规则是可以为空的。只要这个规则存在于页面上,那么MediaQueryList对象就可以正确更新,并且所有通过addListener()绑定的事件都可以正确触发。
你可以使用JavaScript来修复这个问题:
var style = document.createElement("style");
style.appendChild(document.createTextNode("@media screen and (max-width:600px) { .foo {} }"));
document.head.appendChild(style); //WebKit支持document.head
当然你或许会需要为每一个使用matchMedia()访问的media query应用这个修复,这可能会显得有些繁杂。
Firefox中的实现也有一些怪异的特性。理论上,你可以注册一个处理器监听query状态的改变,这样你就不需要一直保持对MediaQUeryList对象的引用了,如下:
//在Firefox中无效
window.matchMedia("screen and (max-width:600px)").addListener(function(mql) {
console.log("Changed!");
});
在Firefox中,即使media query是有效的,监听器或许仍然不会被调用。在我的测试中,它可能会被触发0到3次,然后再也不会被触发。Firefox团队已经了解了这个bug,并且应该很快会修复这个问题。所以,在使用这个方案的同时,你还是需要继续引用MediaQueryList对象来确保监听器被触发了:
//fix for Firefox
var mql = window.matchMedia("screen and (max-width:600px)");
mql.addListener(function(mql) {
console.log("Changed!");
});
因为mql的存在,监听器就一直可以被触发。
在我的上一篇文章中,由于对一部分内容的误解,我对media query的描述有一些错误。监听器会在两个情景下被触发:
这就是为什么需要将MediaQueryList对象传入到监听器中,这样你可以通过检查matches属性来确定media query是否有效。例如:
mql.addListener(function(mql) {
if (mql.matches) {
console.log("Matches now!");
} else {
console.log("Doesn't match now!");
}
});
通过这样的代码,你就可以监控一个web应用的状态,从而可以做一些适当的调整。
在我在了解matchMedia()的时候,我试图创建一个函数模拟matchMedia()(原文称为polyfill,作为一种不支持matchMedia()方法情况下的替代措施)。Paul Irish使用类似我最近一篇文章里描述的技术实现了一个polyfill。Paul Hayes之后开设了一个分支来创建polyfill,使用了一个基于简单CSS transition的监听器来检测改变。然而,由于它依赖于CSS transitions,监听器的兼容性受到CSS transition兼容性的限制。加上调用matches不能再次获取浏览器状态与Firefox和webkit中的bug,让我坚信创建一个polyfill并不是一个正确的方向。毕竟,当真实环境中存在那么多明显的bug时,polyfill又怎么能正确执行。
我选择的方式是创建一个容器来包裹API的行为,从容器处消除这些问题。当然,我选择将这个API作为YUI Gallery的一个模块实现,叫做gallery-media。这个API很简单,并由两个方法组成。第一个是Y.Media.matches(),调用media query字符串并返回匹配结果。不需要记录任何对象,只需要直接这样获取:
var matches = Y.Media.matches("screen and (max-width:600px)");
第二个方法是Y.Media.on(),它允许你来指定一个media query和一个监听器,当media query生效或者失效时执行。监听器的参数是一个含有matches和media属性的对象,提供media query的信息。例如:
var handle = Y.Media.on("screen and (max-width:600px)", function(mq) {
console.log(mq.media + ":" + mq.matches);
});
//detach later
handle.detach();
取代使用CSS transitions来监控改变,我使用了一个简单的onresize事件处理器。在桌面系统中,浏览器的尺寸是最有可能被改变的(在移动设备中,方向也有可能被该改变),那么我为旧的浏览器做了这么一个假设。API使用了原生的matchMedia()函数,在WebKit和Chrome中都有效并进行了一些修复,从而可以得到稳定的行为。
JavaScript中的CSS media query比我想象的复杂一些,但是非常实用。我不觉得通过polyfill的方式修复marchMedia()是一个好主意,它让你不能在多个浏览器中使用相同的方式编码。好处是,它将你从bug和改变中隔离出来,从而看起来会更加先进一些。现在让我们使用JavaScript和CSS media query创造更多的可能。
前一篇:【译】CSS media queries在JavaScript中的应用(一)
扫码关注w3ctech微信公众号
共收到0条回复