Category: JavaScript

Baffling IE Behavior with PNG Opacity Fix

This has been baffling me to no end for the past few days. I’ve exhausted my own skill to figure the problem out, so I’m putting it up here in the hopes that someone more knowledgeable than I can solve the puzzle.

The other day I was browsing message board posts and someone was asking for information on how to get Internet Explorer to play nice with variable opacity PNG images. I suggested that she try Andrew Gregory‘s Improved PNG Behavior file for IE which the poster did. However, she reported the most unusual thing: when implemented on her page, all of her images vanished from view!

Puzzled, I made my own (very ugly) test pages for three different versions of the behavior file. Surprisingly, when I tested Andrew’s behavior file the PNG image on my page vanished too! So I dove into the the code and traced the problem down to line 54 of his file, which reads: element.runtimeStyle.filter = ''; // remove filter.

Commenting that line out solved the disappearing image problem, but it seemed like a messy hack more than a fix. I had to assume he put the line in there for a reason. So I decided to litter the functions in the file with alert()‘s to help me visualize the problem. Then I noticed the most bizarre thing. Inside the fixImage() function, both the if block and the else block were executing procedurally. That is, first the statements in the if block executed and applied the appropriate IE filter to the PNG image, but then the else block was executing right afterwards and was removing the filter even though the if block had evaluated to a boolean true!

Here is the relevant code I was working with (some comments, and the alert()s added by me):

function fixImage() {
  // check for real change
  if (realSrc && (element.src == realSrc) && IS_PNG.test(element.src)) {
    element.src = blankSrc;
    alert("element.src set to " + blankSrc); // help debug
  } else {
    if (element.src != blankSrc) {
      // backup old src
      realSrc = element.src;
    }
    // test for png
    if (realSrc && IS_PNG.test(realSrc)) {
      alert("fixImage() 'test for png' IF statement executing"); // help debug
      element.src = blankSrc;
      element.runtimeStyle.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + realSrc + "',sizingMethod='scale')";
    } else {
/*
?? It apparently causes any fix[ed]Image() to disappear! Not sure why it even executes,
?? because the if block above executes too...and doesn't that mean that the else
?? statement should not execute?!?!
*/
      alert("Inside fixImage() 'test for png' else statement: removing filter..."); // help debug
      element.runtimeStyle.filter = ''; // remove filter
    }
  }
}

So now I’m really puzzled. What’s going on? I emailed Andrew on the matter and he said that he couldn’t replicate this behavior. I am testing this in the latest Internet Explorer 6 on Windows XP Home with Service Pack 2 installed. (Or, to be completely anally retentive, version 6.0.2900.2180.xpsp_sp2_rtm.040803-2158 as reported by Help → About Internet Explorer.) He was using the latest IE 6 on Windows 2000, however, so maybe the two versions are different somehow.

All the code is in my PNG test directory and the relevant page is the improved_pngtest.html document. The .htc file is called from within that document. If anyone out there is bored or wants to try figuring out what’s happening in IE, I’d be very interested to hear about your findings.

I should also point out that Andrew has updated and re-written some parts of this function his new file works without any problems even on my configuration. I’d still like to figure out why IE is executing else statements after their respective if blocks have executed though.

Keeping Presentation out of Behavioral JavaScripting

Lately I’ve been working on a personal project of mine, redesigning and revitilizing my website about Bipolar Disorder. It’s still deeply entrenched in the redesign and I’m not even done with the site templates yet, but I was anxious to get some content rolling out quickly so I went ahead with it anyway.

Some elements of the design, however, relied on dynamic scripting to style appropriately. The key thing here, however, is that I wanted to avoid accessing or manipulating style elements from within the JavaScript script. In other words, I wanted to ensure that all my style rules, the visual declarations for the presentation of these links, would be kept in the site’s CSS.

There were several reasons for this:

  1. To keep presentation clearly separated from behavior and structure.
  2. To keep the JavaScript unobtrusive and portable.
  3. To ensure that both aspects, presentation and behavior could be easily updated or altered in the future.
  4. To allow for various styling without touching the script.

Typically, a JavaScript script with a line similar to elem.style.property = 'value'; is used to create so-called “dynamic styles.” Unfortunately, this would not do for me.

So I fiddled and found that the best way to go about this was to simply tag links that I wanted to style by adding a word to their class name and then write styles for the selected elements inside my stylesheets. In effect, links in my page will be transformed from <a href="http://some.other.site/"></a> to <a class="external" href="http://some.other.site/"></a> So I sat down and wrote this little plug-and-play JavaScript to tag the links I wanted. Let’s go over it line-by-line. (If you’re antsy, here’s the whole script.)

First, we define a function named catchExternalLinks. Then we set a variable, extClassName, to hold the class name we’re going to add to the links. While not strictly necessary to hold in a variable, it does make for easy editing later. Don’t like the word “external” for a class name? Change it to something else.

function catchExternalLinks()
{
    var extClassName = 'external';

Next, we do some object detection. This is to ensure that the browser can handle what we’re going to ask of it. Browsers that can’t handle it won’t try to, which is good because it means visitors won’t see an error when they visit the site. They just won’t see the dynamic styling.

if (document.links && document.getElementById) {

Now that we know we’re talking only to browsers which can handle our instructions, we define a variable, h (for host), which will store the beginning of the web address that we’re at. On www.maymay.net, h now contains the string http://www.maymay.net.

var h = window.location.protocol + '//' + window.location.host;

Next, we need to gather all the links in our page. We assign the links array to the variable l.

var l = document.links;

We need to work with each link separately, so we loop through the links…

for (var i = 0; i < l.length; i++) {

…and assign the value of the href property to the target variable after turning the string toLowerCase text.

var target = l[i].href.toLowerCase();

We only want to work with real links, so first we make sure we’re not dealing with javascript: directives.

if (target.substr(0, 11) != 'javascript:') {

Then we search the target string of the link for the h string. If we don’t find it…

if (target.substr(0, h.length).indexOf(h) == -1) {

…then this link is an external link so we tag it as such by adding the extClassName to its className preceded by a space. We do this instead of using setAttribute() because this way we can keep any pre-existing values for the class attribute already in the link. The key here is to know that className accesses the class attribute of an element.

    l[i].className += ' ' + extClassName;}

Finally, after closing all our blocks properly, we set the catchExternalLinks function to execute onload.

            }
        }
    }
}
window.onload = catchExternalLinks;

Of course, as per the requirements for this script being as unobtrusive as possible, it won’t do anything to the style properties of the link. All it did was add a class value, so we’ll need to declare our styles in our stylesheet. In my CSS page, I write the following to display a little icon for these links.

a.external {
    padding-right: 15px;
    background: transparent url(extlink.gif) center right no-repeat;
}

The styling possibilities are really rather endless now. Since I can instantly identify external links via the class external, I can also write context-specific styles. For instance, I can limit my styles to only one part of the page with a selector such as

#main a.external { ... }

or I could write different styles for external links for the sidebar and a comment on my entry with

#sidebar a.external { ... }
.blogComment a.external { ... }

or any other styling I see fit. In addition, from the script, it’s very easy to test for links that meet a specific criteria, say, Google definition searches, simply by adding another if clause inside the main loop. Here’s a version that does just that.

All together, it goes like this:

function catchExternalLinks()
{
    var extClassName = 'external';  // the class to set for external links
    var defClassName = 'defSearch'; // the class to set for definition searches
    if (document.links && document.getElementById)
    {
        var h = window.location.protocol + '//' + window.location.host;
        var l = document.links;
        for (var i = 0; i < l.length; i++)
        {
            var target = l[i].href.toLowerCase();
            if (target.substr(0, 11) != 'javascript:') // only work on links that aren't JavaScript directives
            {
                // tag external links
                if (target.substr(0, h.length).indexOf(h) == -1)
                {
                    l[i].className += ' ' + extClassName;
                }
                // tag Google definition search links
                var anchor = l[i].childNodes[0].nodeValue; // anchor now contains the anchor text of the link
                var s = '?q=define:' + anchor;
                if (target.substr(0, target.length).indexOf(s) != -1) // use != to ensure that the string (var s) EXISTS in target
                {
                    l[i].className += ' ' + defClassName;
                    l[i].title = 'Definitions for ' + anchor + ' on the Web.'; // for the link tooltip
                }
            }
        }
    }
}
window.onload = catchExternalLinks;

Feel free to steal this snippet. Just remember to write your styles in a stylesheet that you connect to your page, or the script won’t have any noticeable effect. Of course, that’s the whole point. Enjoy! ;)