1

I am currently trying to make my own polyfill that will allow older browsers such as Internet Explorer the ability to read and execute media queries using traditional css selectors. The process flow goes something like this:

  1. Iterate through each stylesheet found within document
  2. Run through each style rule in stylesheet searching for media queries
  3. Define what device size we are targeting then apply appropriate "id"
  4. Dynamically add css line with selector to existing stylesheet

The idea is that the polyfill will search for media queries then apply a parent "id" to the body corresponding to the device size, for instance:

#tablet .wrap { ... }
#mobile .wrap { ... }
#desktop .wrap { ... }

Here is what the javascript looks like so far:

var styleSheets = document.styleSheets;
var debug = false;
var ruleParts = mediaPart = rules = compiledSel = className = '';
var dimS = dimB = 0;
var idList = Array('smallMobile','mobile','tablet','smallDesktop','desktop');

// run through each stylesheet
for( i = 0; i < styleSheets.length; i++ ) {

    // If uncommented will show each stylesheets rules contained therein
    if( debug ) {
        console.log( styleSheets[i].cssRules );
    }

    // run through each rule declaration
    for( a = 0; a < styleSheets[i].rules.length; a++ ) {

        if( styleSheets[i].rules[a].type == 4 ) {

            mediaPart = styleSheets[i].rules[a].media[0].split(' and ');

            dimS = parseInt( mediaPart[0].replace(/[():A-Za-z$-]/g, "") );
            dimB = parseInt( mediaPart[1].replace(/[():A-Za-z$-]/g, "") );

            if( dimS > 0 && dimB < 418 ) {
                className = idList[0];
            } else if( dimS > 419 && dimB < 767 ) {
                className = idList[1];
            } else if( dimS > 768 && dimB < 1024 ) {
                className = idList[2];
            } else if( dimS > 1025 && dimB < 1201 ) {
                className = idList[3];
            } else {
                className = idList[4];
            }


            if( styleSheets[i].rules[a].cssRules.length > 1 ) {

                for( b = 0; b < styleSheets[i].rules[a].cssRules.length; b++ ) {

                    ruleParts = styleSheets[i].rules[a].cssRules[b].cssText.split('{');
                    rules = ruleParts[1].split('}');

                    addCSSRule( styleSheets[i], '#'+ className +' '+ ruleParts[0], rules[0], 1 );

                    /*
                     *      Investigate why the .insertRule() and addRule() are failing specifically what is causing them to break
                     *      
                     */

                }

            } else {

            }

        }

    }
}

function addCSSRule(sheet, selector, rules, index) {
    if(sheet.insertRule) {
        sheet.insertRule(selector + "{" + rules + "}", index);
    } else {
        sheet.addRule(selector, rules, index);
    }
}

Right now everything is working the way I want, even adding the css rule to the style sheet however I get this error and I'm not totally sure at what point:

Uncaught TypeError: Cannot read property 'length' of undefined 

If I remove the function call to this line, I don't get the error:

addCSSRule( styleSheets[i], '#'+ className +' '+ ruleParts[0], rules[0], 1 );

So I'm not sure what is happening during the function call and the reading of the .length

My html looks something like this:

<!DOCTYPE html>
<head>
<title>Style Manipulation VIA Javascript</title>

<link rel="stylesheet" href="css/test.css" />
<link rel="stylesheet" href="css/typo.css" />

</head>
<body>

<div class="wrap">
    <p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo. Quisque sit amet est et sapien ullamcorper pharetra. Vestibulum erat wisi, condimentum sed, commodo vitae, ornare sit amet, wisi. Aenean fermentum, elit eget tincidunt condimentum, eros ipsum rutrum orci, sagittis tempus lacus enim ac dui. Donec non enim in turpis pulvinar facilisis. Ut felis. Praesent dapibus, neque id cursus faucibus, tortor neque egestas augue, eu vulputate magna eros eu erat. Aliquam erat volutpat. Nam dui mi, tincidunt quis, accumsan porttitor, facilisis luctus, metus</p>
    <p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo. Quisque sit amet est et sapien ullamcorper pharetra. Vestibulum erat wisi, condimentum sed, commodo vitae, ornare sit amet, wisi. Aenean fermentum, elit eget tincidunt condimentum, eros ipsum rutrum orci, sagittis tempus lacus enim ac dui. Donec non enim in turpis pulvinar facilisis. Ut felis. Praesent dapibus, neque id cursus faucibus, tortor neque egestas augue, eu vulputate magna eros eu erat. Aliquam erat volutpat. Nam dui mi, tincidunt quis, accumsan porttitor, facilisis luctus, metus</p>
</div>

Overall I need to figure out why the property length can't be read and what in the addCSSRule function call is causing the error.

4

1 回答 1

1

styleSheets 对象的 IE 和 W3C 实现之间存在显着差异,希望以下内容足以提供帮助:

function getStyleRules() {
    var rule, rules;
    var sheet, sheets = document.styleSheets;
    var ruleCssText = ['Rules:','',]

    for (var i=0, iLen=sheets.length; i<iLen; i++) {
      sheet = sheets[i];

      // Get the rules using:
      //
      //        W3C model        IE model
      rules = sheet.cssRules || sheet.rules;

      for (var j=0, jLen=rules.length; j<jLen; j++) {
        rule = rules[j];

        // The selector is available here in both models,
        // but uppercase in IE so set to lower case
        alert('Selector: ' + rule.selectorText.toLowerCase());

        // Getting the actual rule text
        // W3C model - selector and rule
        if (rule.cssText) {
          ruleCssText.push(rule.cssText);

        // IE model - rule only, doesn't include selector
        } else if (rule.style) {
          ruleCssText.push(rule.style.cssText.toLowerCase());
        }
      }
      alert(ruleCssText.join('\n'));
    }
}

IE 模型的 MSDN 上有文档,但我不确定它是否仍涵盖较旧的 IE 或仅适用于两种模型的较新版本: styleSheet objectrule object

这里有一些 Opera 文档: Dynamic style - manipulating CSS with JavaScript

以及这里的一些信息: styleSheet objectCSS Rule object

与所有 Web 资源一样,请以严重的怀疑态度对待它,寻找其他资源并在尽可能多的浏览器(尤其是较旧的浏览器)中进行测试、测试、测试。

运行您的代码:

> TypeError: styleSheets[i].rules is undefined
> 
> for( a = 0; a < styleSheets[i].rules.length; a++ ) {

在这里,您混合了 IE(规则)和 W3C(cssRules)模型。最好只获取一次规则对象:-

  var sheetRules = styleSheets[i].rules || styleSheets[i].cssRules;
  for( a = 0; a < sheetRules.length; a++ ) {

然后你有:

  if ( sheetRules[a].type == 4 ) {

但是 IE 模型没有实现规则对象的类型属性(即使 MSDN 文档说它实现了,这是 IE 9 及更高版本实现的 W3C 模型)。

然后:

>     mediaPart = sheetRules[a].media[0].split(' and ');

请注意,媒体是工作表的属性,而不是规则的属性,因此:

      // Perhaps mediaParts (plural)?
      mediaPart = styleSheets[i].media;

更糟糕的是,IE 模型有一个带有字符串类型的媒体属性,但它是空的。您可以使用以下方法获取它的文本:

styleSheets[0].cssText.replace(/\s+/g,' ').match(/@media.+}[^{]+}/)

.

>     // TypeError: mediaPart[1] is undefined
>     dimB = parseInt( mediaPart[1].replace(/[():A-Za-z$-]/g, "") );

在这里,您似乎期望“和”出现在规则中。您没有提供规则示例,但如果它们是这样的:

@media screen, print { ... }

那么media是一个媒体规则列表:media[0]is screenmedia[1]is print,所以你想迭代媒体规则。

无论如何,这已经足够了。

于 2013-10-16T02:44:17.670 回答