Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
248 views
in Technique[技术] by (71.8m points)

javascript - Check if css property has !important attribute applied

If I have a style like this -

?div#testdiv {position:absolute;top:10px !important;}?

I can query the top value with jQuery like this -

$("#testdiv").css("top");

which will return the value 10px. Is it possible to use jQuery or JavaScript to check if the top property has had the !important attribute applied to it?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

First of all, such a solution does not seem to exist in jQuery.

Many available javascript solutions offered, use the function getPropertyPriority(). First, this function is not supported by IE6-IE8 ( see here and here). Second, this function does not directly work on elements if their style is not declared inline. So, we would be able to get the important property in the following case:

<div id="testdiv" style="top : 10px !important;">Some div</div>
<script type="text/javascript">
// should show 'important' in the console.
console.log(document.getElementById("testdiv").style.getPropertyPriority('top'));
</script>

However if we could declare the style of #testdiv in a css stylesheet, we will get an empty string. Also the CSSStyleDeclaration interface is not available in IE6-8. Ofcourse this is pretty useless this way. We need a different approach.

I've put this approach into a JSFiddle. We can read the !important property directly from the css stylesheets, which are contained in the array document.styleSheets[]. (Opera 8 and below do not support this array). At Quirksmode you can see the methods which methods are supported to access the stylesheets. Based on this information we can do the following:

  • For IE6-8, we use the styleSheets[].imports to access the imported stylesheets (and keep doing this recursively till we do not find any import statements anymore) and then styleSheets[].rules basically for each stylesheet add the css rules to an array.
  • For other browsers, we use styleSheets[].cssRules to access both the imported and css rules. We detect the import rules by checking if it implements the CSSImportRule interface and use these to access the css rules in the imported stylesheets recursively.

In both cases we add the css rules to an array only if the rules matches the HTMLElement (in your case #testdiv). This results in an array of css rules that are matched to a HTMLElement. This is basically what the getMatchedCSSRules() function in webkit browsers does. However, we write it ourselves here.

Based on this information we write our hasImportant(htmlNode, property) function, where htmlNode is an HTMLElement (your testdiv) and property the css property ('top' in your case). First, we check if the inline style of the top property has an important attribute. This saves us looking through the stylesheets if it does contain this attribute.

We write a new function isImportant(node, property) which uses our good old function node.style.getPropertyPriority(property). However, like I mentioned earlier in this answer: this function is not supported in IE6-IE8. We can write the function ourselves: in IE the property node.style.cssText contains the declaration block text. We search for the property ('top') in this block of text and check if its value contains '!important'. We can reuse this function on every css rule obtained using the getMatchedCSSRules function, by looping through all css rules that match with the htmlNode and calling the isImportant function.

All of the above can be found in the code below. This is the basic approach and probably should be fine-tuned further:

  • some code might be replaced with jQuery
  • some code might be simplified
  • css rules implementing the CSSMediaRule interface and other interfaces might cause some problems for this code and an error check should be performed
  • there might be simpler approach, but I am not aware of any other method to get this working cross browser.

    var debug = true;
    
    /**
     * Get the css rules of a stylesheet which apply to the htmlNode. Meaning its class
     * its id and its tag.
     * @param CSSStyleSheet styleSheet
     * @param HTMLElement htmlNode
     */
    function getCssRules(styleSheet, htmlNode) {
        if ( !styleSheet )
            return null;
    
        var cssRules = new Array();
        if (styleSheet.cssRules) {
            var currentCssRules = styleSheet.cssRules;
            // Import statement are always at the top of the css file.
            for ( var i = 0; i < currentCssRules.length; i++ ) {
                // cssRules all contains the import statements.
                // check if the rule is an import rule.
                if ( isImportRule(currentCssRules[i]) ) {
                    // import the rules from the imported css file.
                    var importCssRules = getCssRules(currentCssRules[i].styleSheet, htmlNode);
                    if ( importCssRules != null ) {
                        // Add the rules from the import css file to the list of css rules.
                        cssRules = addToArray(cssRules, importCssRules, htmlNode);
                    }
                    // Remove the import css rule from the css rules.
                    styleSheet.deleteRule(i);
                }
                else {
                    // We found a rule that is not an CSSImportRule
                    break;
                }
            }
            // After adding the import rules (lower priority than those in the current stylesheet),
            // add the rules in the current stylesheet.
            cssRules = addToArray(cssRules, currentCssRules, htmlNode);
        }
        else if (styleSheet.rules) {
            // IE6-8
            // rules do not contain the import statements.
            var currentCssRules = styleSheet.rules;
    
            // Handle the imports in a styleSheet file.
            if ( styleSheet.imports ) {
                // IE6-8 use a seperate array which contains the imported css files.
                var imports = styleSheet.imports;
                for ( var i = 0; i < imports.length; i++ ) {
                    var importCssRules = getCssRules(imports[i], htmlNode);
                    if ( importCssRules != null ) {
                        // Add the rules from the import css file to the list of css rules.
                        cssRules = addToArray(cssRules, importCssRules, htmlNode);
                    }
                }
            }
            // After adding the import rules (lower priority than those in the current stylesheet),
            // add the rules in the current stylesheet.
            cssRules = addToArray(cssRules, currentCssRules, htmlNode);
        }
    
        return cssRules;
    }
    
    /**
     * Since a list of rules is returned, we cannot use concat. 
     * Just use old good push....
     * @param CSSRuleList cssRules
     * @param CSSRuleList cssRules
     * @param HTMLElement htmlNode
     */
    function addToArray(cssRules, newRules, htmlNode) {
        for ( var i = 0; i < newRules.length; i++ ) {
            if ( htmlNode != undefined && htmlNode != null && isMatchCssRule(htmlNode, newRules[i]) )
                cssRules.push(newRules[i]);
        }
        return cssRules;
    }
    
    /**
     * Matches a htmlNode to a cssRule. If it matches, return true.
     * @param HTMLElement htmlNode
     * @param CSSRule cssRule
     */
    function isMatchCssRule(htmlNode, cssRule) {
        // Simply use jQuery here to see if there cssRule matches the htmlNode...
        return $(htmlNode).is(cssRule.selectorText);
    }
    
    /**
     * Verifies if the cssRule implements the interface of type CSSImportRule.
     * @param CSSRule cssRule
     */
    function isImportRule(cssRule) {
        return cssRule.constructor.toString().search("CSSImportRule") != -1;
    }
    
    /**
     * Webkit browsers contain this function, but other browsers do not (yet).
     * Implement it ourselves...
     *
     * Finds all matching CSS rules for the htmlNode.
     * @param HTMLElement htmlNode
     */
    function getMatchedCSSRules(htmlNode) {
        var cssRules = new Array();
    
        // Opera 8- don't support styleSheets[] array.
        if ( !document.styleSheets )
            return null;
    
        // Loop through the stylesheets in the html document.
        for ( var i = 0; i < document.styleSheets.length; i++ ) {
            var currentCssRules = getCssRules(document.styleSheets[i], htmlNode)
            if ( currentCssRules != null )
                cssRules.push.apply(cssRules, currentCssRules);
        }
    
        return cssRules;
    }
    
    /**
     * Checks if the CSSStyleRule has the property with 'important' attribute.
     * @param CSSStyleRule node
     * @param String property
     */
    function isImportant(node, property) {
        if ( node.style.getPropertyPriority && node.style.getPropertyPriority(property) == 'important' )
            return true;
        else if ( node.style.cssText && getPropertyPriority(node.style.cssText, property) == 'important' ) {
            // IE6-8
            // IE thinks that cssText is part of rule.style
            return true;
        }
    }
    
    /**
     * getPropertyPriority function for IE6-8
     * @param String cssText
     * @param String property
     */
    function getPropertyPriority(cssText, property) {
        var props = cssText.split(";");
        for ( var i = 0; i < props.length; i++ ) {
            if ( props[i].toLowerCase().indexOf(property.toLowerCase()) != -1 ) {
                // Found the correct property
                if ( props[i].toLowerCase().indexOf("!important") != -1 || props[i].toLowerCase().indexOf("! important") != -1) {
                    // IE automaticaly adds a space between ! and important...
                    return 'important'; // We found the important property for the property, return 'important'.
                }
            }
        }
        return ''; // We did not found the css property with important attribute.
    }
    
    /**
     * Outputs a debug message if debugging is enabled.
     * @param String msg
     */
    function debugMsg(msg) {
        if ( debug ) {
            // For debugging purposes.
            if ( window.console )
                console.log(msg);
            else
                alert(msg);
        }
    }
    
    /**
     * The main functionality required, to check whether a certain property of 
     * some html element has the important attribute.
     * 
     * @param HTMLElement htmlNode
     * @param String property
     */
    function hasImportant(htmlNode, property) {
    
        // First check inline style for important.
        if ( isImportant(htmlNode, property) ) {
            // For debugging purposes.
            debugMsg("Inline contains important!");
            return true;
        }
    
        var rules = getMatchedCSSRules(htmlNode);
    
        if ( rules == null ) {
            debugMsg("This browser does not support styleSheets...");
            return false;
        }
    
        /**
         * Iterate through the rules backwards, since rules are
         * ordered by priority where the highest priority is last.
         */
        for ( var i = rules.length; i-- > 0; ) {
            var rule = rules[i];
    
            if ( isImportant(rule, property) ) {
                // For debugging purposes.
                debugMsg("Css contains important!");
                return true;
            }
    
        }
        return false;
    }
    
    $(document).ready(function() {
        hasImportant($('#testdiv')[0], 'top');
    });
    

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...