views:

426

answers:

1

I'm using a jquery globalcss plugin to change global stylesheets. It doesn't handle opacity and IE.

I've been trying to get it to work with no luck. Here's my attempt to force the plugin to try to understand IE style opacity.

function changeCss (property, value, target) {
    if (property === "opacity") {
     $(target).globalcss("filter","alpha(opacity="+value*100+")"); 
     /* For IE 8 (and 9, 10, 11?). Don't miss the added quotes */
     $(target).globalcss("-MS-filter","\"progid:DXImageTransform.Microsoft.Alpha(Opacity="+value*100+")\""); 

    }
    $(target).globalcss(property,value);
}

Blah. If anyone can help, that would be great. Thanks.

I'm pasting the plugin here because it's no longer on its original site:

/*
 * Global Stylesheet jQuery Plugin
 * Version: 0.1
 * 
 * Enables CSS modification that uses a 'global' stylesheet, rather than inline CSS.
 *
 * Copyright (c) 2009 Jeremy Shipman (http://www.burnbright.co.nz)
 * 
 * Dual licensed under the MIT and GPL licenses:
 *   http://www.opensource.org/licenses/mit-license.php
 *   http://www.gnu.org/licenses/gpl.html
 * 
 * INSTRUCTIONS:
 * use in the same way as the jQuery css function. Eg:
 *  $("some selector").globalcss("style","value");
 *
 * use the globalsetylesheet.print() function to return a string of the global stylesheet
 */
(function($) {

    //global singleton class for 
    globalstylesheet = new function globalStylesheet(){
     if(!document.styleSheets){
      alert("document.Stylesheets not found");
      return false;
     }

     var sheet = null;
     var setrules = Array(); // rule cache

     //set up a dummy noded
     var cssNode = document.createElement('style');
     cssNode.type = 'text/css';
     cssNode.rel = 'stylesheet';
     cssNode.media = 'screen';
     cssNode.title = 'globalStyleSheet';
     document.getElementsByTagName("head")[0].appendChild(cssNode);

     //find the newly created stylesheet and store reference
     for(var i = 0; i < document.styleSheets.length; i++){
      if(document.styleSheets[i].title == "globalStyleSheet"){
       sheet = document.styleSheets[i];
      }
     }

     //set a CSS rule
     this.setRule = function setRule(selector,ruleText){
      if(setrules[selector] != undefined){
       return setrules[selector];
      }else{
       if(sheet.addRule){ // IE
        sheet.addRule(selector,ruleText,0);
       }else{
        sheet.insertRule(selector+'{'+ruleText+'}',0);
       }
       setrules[selector] = this.getRule(selector);
      }
      return setrules[selector];
     }

     //get a saved CSS rule
     this.getRule = function getRule(selector){
      if(setrules[selector] != undefined){
       return setrules[selector];
      }else{
       var rules = allRules();
       for(var i = 0; i < rules.length; i++){
        if(rules[i].selectorText == selector){
         return rules[i];
        }
       }
      }
      return false;
     }

     //helper function to get all rules
     function allRules(){
      if(sheet.cssRules){ //IE
       return sheet.cssRules;
      }else{
       return sheet.rules;
      }
     }

     //print out the stylesheet
     this.print = function print(){
      var styleinfo = "";
      var rules = allRules();
      for(var i = 0; i < rules.length; i++){
       styleinfo+= rules[i].cssText+"\n"
      }
      return styleinfo;
     }

     //use jQuery's css selector function to set the style object
     this.css = function css(jquery,key,value){
      rule = this.setRule(jquery.selector,key+":"+value+";");
      jQuery(rule).css(key,value); 
     }
    }

    //hook new function into jQuery
    jQuery.fn.extend({
     globalcss : function globalcss(key,value){
      globalstylesheet.css(this,key,value);
     }
    });

})(jQuery);

Edit: I created a jsbin live demo. Please compare in different browsers. http://jsbin.com/iqadu/edit

+2  A: 

UPDATE:

My first idea below wasn't the problem. Turns out the problem is much more pedestrian: make sure to add a zero before floating-point numbers less than 1. Perhaps this is due to how javascript handles conversion of string values to numbers? Regardless, adding a zero before ".5" fixed the problem.

    // works
    var property = "opacity";
    var value = "0.5";
    var target = ".transparency";

    // doesn't work
    var property = "opacity";
    var value = ".5";
    var target = ".transparency";

Take a look at working code here: http://jsbin.com/ikore3. BTW, your original demo page had a javascript issue with braces in the wrong place. I fixed that too.

BELOW IS ORIGINAL IDEA FOR WHAT WAS CAUSING THIS-- TURNED OUT NOT TO BE THE PROBLEM

You may be running into a quirk of IE which has nothing to do with jquery: in order for opacity to work, elements must have "layout" which is an IE-specific boolean state which is triggered by CSS such as height, width, zoom, etc. If an element does not have "layout" then opacity won't work.

The workaround is to add particular CSS to the element in order to give it "layout". Not sure if this is what's happening in your case, but it's easy enough to check by adding one of the layout-providing CSS attributes and seeing if the problem goes away.

From http://joseph.randomnetworks.com/archives/2006/08/16/css-opacity-in-internet-explorer-ie/:

But simply setting the filter/opacity value in IE isn’t enough. Turns out that IE requires an element to be positioned in order for filter/opacity to work. If your element doesn’t have a position you can work around this by adding ‘zoom: 1‘ to your CSS. There are other hacks to deal with this, zoom was the one I picked and it seems to work well enough.

In JavaScript you can find out if an element in IE has a position component by checking element.currentStyle.hasLayout. If hasLayout is false then the element has no CSS positioning.

If you want to learn more about this issue here is a reading list to get your started:

◦ Exploring OpacityStep-by-Step [broken link]

On having layout

Opacity @ QuirksMode

Using your code, here's one possible way to ensure how to test whether this is your problem or not:

function changeCss (property, value, target) {
    if (property === "opacity") {
        $(target).globalcss("filter","alpha(opacity="+value*100+")");   
        /* For IE 8 (and 9, 10, 11?). Don't miss the added quotes */
        $(target).globalcss("-MS-filter","\"progid:DXImageTransform.Microsoft.Alpha(Opacity="+value*100+")\""); 

        $(target).globalcss("zoom","1");   // ensure the target has layout
    }
    $(target).globalcss(property,value);
}

More sophisticated versions of this would check to see if zoom was already present, and only add it if it's missing. You could also check the hasLayout property and only set the zoom if it was false, as long as you defend against the non-IE case where hasLayout won't be there at all.

If this doesn't fix the problem, could you post an HTML sample or URL, so we can repro the problem which will make it easier to suggest a fix? Thanks!

Justin Grant
Yep, thanks for your help. I created a live demo on jsbin: http://jsbin.com/iqadu/edit
Jourkey
cool-- the demo made things much clearer, turns out the problem was kinda simple: using ".5" instead of "0.5" for the number! See revised answer above. And thanks for the bounty! :-)
Justin Grant