On IE and buttons

1st August, 2008

With regards to Drew’s recent post Internet Explorer’s mishandlings of BUTTONs, I thought I would post a solution I came up with whilst serving at Yahoo! for the new Ask/Answer flow.

What’s the problem?

For those of you who’ve not read Drew’s above–linked post (you should, it explains the problem concisely), the issue can be broken down thusly:

What’s the problem with Drew’s method?

Nothing major, except that it all depends on the way he “(throws) the SPAN off-screen with CSS”. If he’s just using off–left, screen readers will still be able to see the content, which in his example is “ABC123”: whilst this is only present for the example given, could theoretically be anything. Either way it’s unlikely to be relevant to a screen–reader (or a search crawler, when it comes down to it).

As I stated, it’s very minor nit–picking. But what is the Internet for if not to nit–pick (or to call people Nazis)?

So what’s my solution?

Despite Drew being entirely correct when he said that a solution that relies on JavaScript isn’t a solution, I’m going to do one anyway. Key features include:

Note that no CSS is provided: this is left as an exercise for the reader. Also note that it’s written for a site using the YUI library, which is the front–end framework of choice where this code was originally written.

Also bear in mind this this comes with a really really big caveat.

<form action="" method="post">
    <-- other FORM fields before (remembering our LABELs and FIELDSETs!!)-->
    <div>
        <input type="submit" class="cta" name="my-submit" value="Save Application">
    </div>
</form>

<!-- we need the YUI basics -->
<script type="text/javascript"
 src="http://yui.yahooapis.com/2.5.2/build/yahoo-dom-event/yahoo-dom-event.js"></script>
 
<script type="text/javascript">
// namespaced to "EUropean User Interface"
YAHOO.namespace('EU.UI');
// CTA stands for "call to action", a standard Yahoo! term for a rounded corner button
YAHOO.EU.UI.cta = function() {
    function fixSubmit(e) {
        // this function is only fired for browsers which don't understand 
        // button type="submit", so we're okay to proceed without browser 
        // checking
        
        // clobber default behaviour
        YAHOO.util.Event.preventDefault(e);
        
        var form = this.form;
        // IE will submit all buttons at once, so kill/disable the ones we didn't click
        var els = form.getElementsByTagName('button');
        if (els.length > 0) {
            for (var i=0, l=els.length; i<l; i++) {
                // check if it's the same element--name isn't reliable as 
                // there could be multiple forms with the same named element
                if (els[i] !== this) {
                    // note this will cause a "flash" of greyed out buttons
                    els[i].disabled = true;
                }
            }
        }
        
        // now that all buttons except the one that was clicked are disabled, 
        // send the form along with just the button clicked
        form.submit();
    }
    return {
        init : function() {
            var els = YAHOO.util.Dom.getElementsByClassName('cta', 'input');
            // these could be anything--just use them as styling hooks
            var startContent = '<span><span><span><span>';
            var endContent = '<\/span><\/span><\/span><\/span>';
            if (els.length > 0) {
                for (var i=0, l=els.length; i<l; i++) {
                    var el = els[i];
                    if (el.type.toLowerCase() === 'submit') {
                        // do transform
                        var newCta = document.createElement('div');

                        // add additional classes ('secondary', 'right' etc.)
                        newCta.className = el.className;

                        var button = document.createElement('button');

                        button.name     = el.name;
                        button.value    = el.value;
                        button.innerHTML = startContent + el.value + endContent;
                        newCta.appendChild(button);
                        el.parentNode.replaceChild(newCta, el);

                        // fix the submit
                        try {
                            button.type = 'submit';
                        }
                        // IE errors out on the above, so we now know we're dealing with IE
                        catch (err) {
                            YAHOO.util.Event.on(button, 'click', fixSubmit);  
                        }
                        // fix :focus and :active pseudo-class for all IE for keyboard navigation
                        if (YAHOO.env.ua.ie > 0) {
                            YAHOO.util.Event.on(button, 'focus', function(e) {
                                YAHOO.util.Dom.addClass(this, 'hover');
                            });
                            YAHOO.util.Event.on(button, 'blur', function(e) {
                                YAHOO.util.Dom.removeClass(this, 'hover');
                            });
                            YAHOO.util.Event.on(button, 'click', function(e) {
                                YAHOO.util.Dom.addClass(this, 'active');
                            });
                        }
                    }
                }
            }
        }
    }
}();
// above () returns the function immediately, causing the inner functions to be "private"

// fire public init method on DOMReady event
YAHOO.util.Event.onDOMReady(YAHOO.EU.UI.cta.init);
</script>

“You use(d) Javascript for presentation you bad bad man”

Yes I do. Well, I did (I only did this type of thing once). Glad you’ve been paying attention. I believe I’m okay doing this for a few reasons (so listen closely):

You may, as is your wont, disagree. That’s okay, but before I get flamed (which I can’t, given that I still don’t have comments enabled), I want it to be known that this script was produced under the above caveats. As usual, YMMV. Just don’t go doing this type of thing often.

This post tagged with:

Comments are not enabled for this post.