Nested Links

Cats playing nested links

The problem

HTML spec­i­fi­ca­tion has a lot of dif­fer­ent re­stric­tions. And I have my doubts about the fea­si­bil­ity of many of those. One ex­am­ple which I stum­ble upon rather of­ten —nested links.

Spec straightly for­bids such nesting:

The a el­ement

[…]

Con­tent model: trans­par­ent, but there must be no in­ter­ac­tive con­tent de­scendant.

And if you’d do this, browser’s parser won’t un­der­stand you and, as soon as it’d see the open­ing tag for the nested link, it would close the first one right there:

<a href="#Foo">
    Foo
    <a href="#Bar">
        Bar
    </a>
    Baz
</a>

in the eyes of the browser would be some­thing like that —

<a href="#Foo">
    Foo
    </a><a href="#Bar">
        Bar
    </a>
    Baz

And a live ex­ample:

How­ever, there are cases when you’d want to nest one link in­side an­other de­spite the re­stric­tions.

So, once again, while work­ing on one task I stum­bled upon such case. I hap­pen to see and use dif­fer­ent workarounds for it be­fore, like an em­u­la­tion of the nested links with JS (for ex­am­ple, with ba­nal onclick), or po­si­tion­ing one of the links around the shared wrap­per (Look at such so­lu­tion by Harry Roberts), but all those workarounds would­n’t work in all cases, and would­n’t work per­fectly. We’d ei­ther lose the na­tiv­ity of a link, try­ing to em­u­late every­thing from scratch, ei­ther won’t be able to make a workaround work just like prop­erly nested el­e­ments would.

So, af­ter try­ing and weigh­ing all the known workarounds in my head, I found out that I couldn't solve the cur­rent task by any of the workarounds other than full JS em­u­la­tion. But I stopped and de­cided to ex­per­i­ment a bit more.

And —found a proper so­lu­tion. HTML-only one, by the way, the one that gives you a way to nest any num­ber of links one into an­other.

The solution

<a href="#a">
    Foo
    <object type="lol/wut">
        <a href="#b">
            Bar
        </a>
    </object>
    Baz
</a>

What we do there is just plac­ing an ob­ject be­tween those links. Yep, it works: all parsers of mod­ern browsers sud­denly see those links in­de­pen­dently, and won’t break your markup any­more. Hooray.

Why does it work

What are ob­jects, in the­ory? They are some ex­ter­nal en­ti­ties, with the type set by the type at­tribute and the con­tent or a link to it placed into the data at­tribute. And the con­tent be­tween the open­ing and clos­ing ob­ject tags is, in fact (Ac­tu­ally, see the Up­date), a fall­back, and it would be shown only when browser would­n’t be ca­pa­ble of dis­play­ing the ob­ject de­fined in the at­trib­utes. Like, for ex­am­ple, if you won’t have an in­stalled plugin.

And if you’d write some gib­ber­ish MIME-type into the type at­tribute, a browser would­n’t un­der­stand it and would go straight to dis­play­ing the fall­back. And, in fact, it would do the same even if you’d omit those “re­quired” at­trib­utes at all.

This way, af­ter wrap­ping any HTML with such at­tribut­e­less <ob­ject> we would get just a wrap­per el­e­ment for this con­tent. But a wrap­per with an un­usual trait: any con­tent in­side of it would be treated by browser’s parser with­out look­ing at the ob­jec­t’s con­text. So, us­ing this trait we can, fi­nally, nest one link into an­other, sep­a­rat­ing them for a parser.

I sup­pose that this be­hav­ior was in­tro­duced to browsers be­cause those ob­ject fall­backs were mostly used for show­ing links like “You don’t have our mar­velous plu­gin in­stalled, down­load it now!” (like, for flash ob­jects). And a lot of de­vel­op­ers could use such ob­jects as any other con­tent or im­ages —nest­ing into all the other tags they had in HTML: links, head­ers, para­graphs, wher­ever. So, browsers needed this fall­back to dis­play prop­erly in those con­di­tions, and they in­tro­duced this be­hav­ior, so the “web­mas­ters” would be safe from break­ing their sites when in­sert­ing some copy-and-pasted code for such ex­ter­nal ob­jects into their pages.

Browser support

Not all of the browsers had this be­hav­ior from the start.

  • In­ter­net Ex­plorer be­gan to be­have like this only from the 9th version.

  • Fire­fox —from the 4th.

  • Opera —at least from 9th (maybe even ear­lier —I did­n’t dig deeper than that).

  • We­bkits —all that I checked: Sa­fari at least from 5.1, Chrome —from 14, etc.

Ob­vi­ously, the only browsers we’d need to sup­port with such con­di­tions are old IE, all other browsers al­ready be­have cor­rectly in all of the widely sup­ported ver­sions.

IE fallback

I don’t know of any easy so­lu­tion for this prob­lem in old IE. At the very least you could try to fix it some­how by “re­mov­ing” the nested links us­ing con­di­tional com­ments:

<a href="…">
    content of the main link…
    <object>
        <!--[if gte IE 9]><!--><a href="…"><!--<![endif]-->
            content of the nested link…
        <!--[if gte IE 9]><!--></a><!--<![endif]-->
    </object>
</a>

You’d lose some func­tion­al­ity there, but it could be ok for the most cases. If not (Cu­ri­ous ones could think about if it is pos­si­ble to make a fall­back us­ing ex­pres­sions), you could then in­sert those links later, sep­a­rately, us­ing the same con­di­tional com­ments, or use other workarounds for this problem.

Is it valid?

Nope, not even close. It is not valid be­cause we don’t have any of the re­quired at­trib­utes on an ob­ject. We could set some dummy, but valid mime-type, like type="lol/​wut", and the ob­ject it­self would then pass the val­i­da­tion, but as soon as we nest the link in­side of it, the val­ida­tor would throw us an error.

Ob­vi­ously, a val­ida­tor is a tool not show­ing any­thing but the for­mal spec­i­fi­ca­tions com­pli­ance. In our case, the us­age of links in­side ob­jects is en­tirely rea­son­able and won’t break any­thing for any­one if we’d make things in a proper way.

More than that, I do not see any rea­sons at all why specs should­n’t drop those re­stric­tions and al­low us to nest the links. No rea­sons. None. There are a lot of cases where this is a cru­cial re­quire­ment, and right now all we have are workarounds and “hacks” like this one.

Usage examples

At first, I wanted to de­scribe all the pos­si­ble use cases for the nested links, with live ex­am­ples and what­not, but then I re­mem­bered that such ex­am­ples won’t con­vince any­one who is­n’t al­ready con­vinced. Any­one who ever stum­bled upon this prob­lem would have every­thing they need from the so­lu­tion above, and oth­ers would al­ways be neg­a­tive about things like that, be­cause specs. Also: it is very tir­ing to mark up all those ex­am­ples, so I’ll just put them into a list:

  • Ex­cerpts from the ar­ti­cles, when the snip­pet of first few sen­tences could con­tain links which you would­n’t want to strip from the markup.

  • Side­notes, foot­notes, and nested terms are all too can be in­side links, why not.

  • Any com­plex UI with a lot of nested en­ti­ties, which could hap­pen to be de­scribed as links. Those could be tweets in any of the Twit­ter’s in­ter­face, lead­ing to the tweet’s page, but at the same time con­tain­ing other links —to users, hash­tags, and ex­ter­nal pages. Those could be mail in­ter­faces, where the snip­pet of a mes­sage in an in­box that should be a link to a mes­sage it­self could con­tain other links —at­tach­ments, link to threads, la­bels, etc.

*

The one thing I’d like to say in the end is that that trick with an ob­ject could be ap­plied to any con­tent that you’d like to use some­where where the specs for­bid you to.

As an ex­am­ple, there are a bunch of new tags in the lat­est specs that you al­ready should know, like de­tails and fig­ure. Guess what: by specs you can use them only in flow-level con­texts: you can’t have pic­tures with cap­tions il­lus­trat­ing some word in­side a para­graph, you can’t have a de­scrip­tion or foot­notes for some words in­side a para­graph or a head­ing (and what other than de­tails tag would fit for this?), you can’t have a lot of cases that some­one do­ing specs could­n’t think of.

The <ob­ject> trick solves all those prob­lems. The ques­tion is only if such us­age would be fea­si­ble for you. And I’d say that a lot of re­stric­tions on specs are use­less and abil­ity to work around them, with valid ar­gu­ments, is priceless.

Update from 2015-03-05

Vladimir Rod­kin found out that Fire­fox’ Flash­block plu­gin re­moves “bro­ken ob­jects”, and it treats at­tribut­e­less <ob­ject> as such. Adding un­known mime-type like type="lol/​wut" fixes this prob­lem and Fx starts to show the ob­ject cor­rectly.