Welcome to my corner of the web, where I post my ponderings, pictures and pontifications.

New Window, No In-source Event

I was prompted by this Twitter post to cross-post an old blog entry of mine.

Having JavaScript events in-source binds you to whatever JavaScript method (and it’s signature) you’ve specified, not to mention breaking the tenets of Progressive Enhancement. When wishing to launch an anchor’s href into a new window while using a STRICT doctype, using window.popup is the only real option, (as the target attribute is invalid in this case). Looking at the W3C specification for anchors, the following could be a solution.

Problem Statement

Recently, a navigation header in one of our solutions had a requirement that all links within its boundary needed to launch their content into a new window, thereby not disturbing any the current page. Since the target attribute is not an option, this can be accomplished by the window.popup method, a fairly standard method within browsers. This meant either the call itself would have to be embedded into the anchor, or a surrogate method. In either case, JavaScript events would have to appear in source:

<a href="http://www.google.com" onclick="window.popup(this.href); return false;">Link 1</a>
<a href="http://www.google.com" onclick="return MyNamespace.popup(this);">Link 2</a>

Problem: Obligations Introduced

The trouble is that whenever script is included in source, it means that you are thereby contractually bound to offer the same method, with the same signature… regardless of circumstance. To give a stronger example, suppose I have the following code:

    <div id="navigation">
        Some content
    </div>
    <script type="text/javascript">
        var myAccordion = new Accordion("navigation")
    </script>

I am now bound by two things:

  1. I must include an Accordion object, even if I don’t need it or if I want to use a different object based on platform (e.g. iPhone).
  2. I must provide the same API as the Accordion object, because there is a global var called myAccordion, which could be referenced elsewhere.

I can still swap out the JavaScript files, but those files must have an object that meet these requirements.

Solution: Progressive Enhancement through Abstraction

It would be far easier if the same markup externalized these specifics and applied event handlers through script:

    <div id="navigation">
        Some content
    </div>
    <script type="text/javascript">
        MyNamespace.setup({
            navigation : "navigation"
        });
    </script>

In this case, an API is defined within a generic namespace object called MyNamepsace. The script block invokes setup, which takes (in this case, which may be overkill) a JSON structure defining the objects or parameters to be used in setup.

This kind of generic abstraction allows us to swap out not only the CSS but also the JavaScript depending on capability or browser.

Proposed Solution: Using the rel Attribute

In the spec, the rel attribute is described as:

This attribute describes the relationship from the current document to the anchor specified by the href attribute. The value of this attribute is a space-separated list of link types.

The spec also says that the value of the rel attribute should be one or more of a set of link-types, but this portion of the spec states:

Authors may wish to define additional link types not described in this specification. If they do so, they should use a profile to cite the conventions used to define the link types. Please see the profile attribute of the HEAD element for more details.

The Proposal

Since the goal is to use JavaScript to “pop” the new window, we need to identify which anchors “pop” their content to a new window, leaving the current document intact. By creating a rel attribute that specifies this, along with the necessary profile, JavaScript methods could distinguish those anchors from other anchors, thus allowing the popup event handlers to be applied.

Creating a new profile

In order to create a new link type, we need[1] to create[2] a profile.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
          "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
    <title>Cerner XHTML Profile</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<dl class="profile">
    <dt id="rel">rel</dt>
    <dd>
        <dl>
            <dt id="forked">forked</dt>
            <dd>
                Refers to a location that should be, if possible, launched
                into a new window or process without disturbing the
                current page or content.
            </dd>
        </dl>
    </dd>
</dl>
</body>
</html>

Using the Profile

Then, we would use the new link-type defined in the profile for any anchor we’d want “popped” into a new window, (or, indeed, whatever other action we choose, based on platform).

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
          "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head profile="http://deploymentserver/contextroot/profile.html">
.
.
.
<ul id="links">
    <li><a href="http://www.cerner.com/" rel="forked">Launch Cerner.com</a></li>
    <li><a href="http://www.google.com/">Go to Google.com</a></li>
</ul>
.
.
.
<script type="text/javascript">
    // Example script; should be namespaced, etc.
    var list = document.getElementById("links"), list.getElementsByTagName("A"),
        event = function() {
            // Process the event for the link, (e.g. popup, alert, redirect, etc).
            alert("Forked page.");
        };

    for(var i = 0; i < links.length; i++) {
        var link = links[i], rel = link.rel;

        if(rel.length <= 0) {
            continue;
        }

        var rels = rel.split(" ");

        for(var a = 0; a < rels.length; a++) {
            var l = rels[a];

            if(l == "forked")) {
                link.onclick = function() {
                    return false;
                };
                // Use my established attachEvent method; it has an EventCache
                // to avoid memory leaks, etc, but is beyond the scope of this post.
                Util.attachEvent(link, "click", event);
                continue;
            };
        }
    }
</script>

References

  1. W3C, link-type Specification
  2. Tantek Çelik, XHTML Meta Data Profiles

Leave a Reply

You can use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Powered by WP Hashcash