jQuery 1.4 Opacity and IE Filters

Posted by Rick Strahl on West-Wind See other posts from West-Wind or by Rick Strahl
Published on Mon, 25 Jan 2010 08:42:18 GMT Indexed on 2010/03/07 23:12 UTC
Read the original article Hit count: 834

Filed under:

Ran into a small problem today with my client side jQuery library after switching to jQuery 1.4. I ran into a problem with a shadow plugin that I use to provide drop shadows for absolute elements – for Mozilla WebKit browsers the –moz-box-shadow and –webkit-box-shadow CSS attributes are used but for IE a manual element is created to provide the shadow that underlays the original element along with a blur filter to provide the fuzziness in the shadow. Some of the key pieces are:

var vis = el.is(":visible");
if (!vis)
    el.show();  // must be visible to get .position

var pos = el.position();
if (typeof shEl.style.filter == "string")
    sh.css("filter", 'progid:DXImageTransform.Microsoft.Blur(makeShadow=true, pixelradius=3, shadowOpacity=' + opt.opacity.toString() + ')');

sh.show()
  .css({ position: "absolute",
      width: el.outerWidth(),
      height: el.outerHeight(),
      opacity: opt.opacity,
      background: opt.color,
      left: pos.left + opt.offset,
      top: pos.top + opt.offset
  });

This has always worked in previous versions of jQuery, but with 1.4 the original filter no longer works. It appears that applying the opacity after the original filter wipes out the original filter. IOW, the opacity filter is not applied incrementally, but absolutely which is a real bummer.

Luckily the workaround is relatively easy by just switching the order in which the opacity and filter are applied. If I apply the blur after the opacity I get my correct behavior back with both opacity:

sh.show()
  .css({ position: "absolute",
      width: el.outerWidth(),
      height: el.outerHeight(),
      opacity: opt.opacity,
      background: opt.color,
      left: pos.left + opt.offset,
      top: pos.top + opt.offset
  });

  if (typeof shEl.style.filter == "string")
      sh.css("filter", 'progid:DXImageTransform.Microsoft.Blur(makeShadow=true, pixelradius=3, shadowOpacity=' + opt.opacity.toString() + ')');

While this works this still causes problems in other areas where opacity is implicitly set in code such as for fade operations or in the case of my shadow component the style/property watcher that keeps the shadow and main object linked. Both of these may set the opacity explicitly and that is still broken as it will effectively kill the blur filter.

This seems like a really strange design decision by the jQuery team, since clearly the jquery css function does the right thing for setting filters. Internally however, the opacity setting doesn’t use .css instead hardcoding the filter which given jQuery’s usual flexibility and smart code seems really inappropriate.

The following is from jQuery.js 1.4:

var style = elem.style || elem, set = value !== undefined;

// IE uses filters for opacity
if ( !jQuery.support.opacity && name === "opacity" ) {
    if ( set ) {
        // IE has trouble with opacity if it does not have layout
        // Force it by setting the zoom level
        style.zoom = 1;

        // Set the alpha filter to set the opacity
        var opacity = parseInt( value, 10 ) + "" === "NaN" ? "" : "alpha(opacity=" + value * 100 + ")";
        var filter = style.filter || jQuery.curCSS( elem, "filter" ) || "";
        style.filter = ralpha.test(filter) ? filter.replace(ralpha, opacity) : opacity;
    }

    return style.filter && style.filter.indexOf("opacity=") >= 0 ?
        (parseFloat( ropacity.exec(style.filter)[1] ) / 100) + "":
        "";
}

You can see here that the style is explicitly set in code rather than relying on $.css() to assign the value resulting in the old filter getting wiped out.

jQuery 1.32 looks a little different:

// IE uses filters for opacity
if ( !jQuery.support.opacity && name == "opacity" ) {
    if ( set ) {
        // IE has trouble with opacity if it does not have layout
        // Force it by setting the zoom level
        elem.zoom = 1;

        // Set the alpha filter to set the opacity
        elem.filter = (elem.filter || "").replace( /alpha\([^)]*\)/, "" ) +
            (parseInt( value ) + '' == "NaN" ? "" : "alpha(opacity=" + value * 100 + ")");
    }

    return elem.filter && elem.filter.indexOf("opacity=") >= 0 ?
        (parseFloat( elem.filter.match(/opacity=([^)]*)/)[1] ) / 100) + '':
        "";
}

Offhand I’m not sure why the latter works better since it too is assigning the filter. However, when checking with the IE script debugger I can see that there are actually a couple of filter tags assigned when using jQuery 1.32 but only one when I use jQuery 1.4.

Note also that the jQuery 1.3 compatibility plugin for jQUery 1.4 doesn’t address this issue either.

Resources

© Rick Strahl, West Wind Technologies, 2005-2010
Posted in jQuery  
kick it on DotNetKicks.com

© West-Wind or respective owner

Related posts about jQuery