Friday, April 21, 2006

Definition Lists Revisited

Ever since playing with the definition list tags (dl, dt, dd) some years ago, I've been looking for good uses.
I often write documents (design docs, API docs, etc) in HTML and usually add a glosary section at the end of the document. The definition lists have been used there but, with no styling or presentational flair.

I saw this the other day and thought it was pretty amazing in its simplicity. I've also liked the sites where certain terms show a definition on mouseover but, I've disliked the abuse of using the title attribute to produce tooltips. I also don't like the duplication that can occur when a term appears multiple times in a page.

So, I thought, why not combine the real contextual meaning of some tags with a little javascript (JScript, ECMA Script, whatever). So, words that should get definitions can be wrapped in definition (dfn) tags, and with some event binding magic, the corresponding definition, dl, can pop-up near the word providing a contextual definition. Page readers will still find the glossary section, so section 508 accessibility should be covered as well. (Update: I've placed a working demo here)

In order to keep the markup simple there should be no special attribute setting to make this work. I'll write some code that will traverse the DOM finding cases where the text of the dfn tag matches the text of a dt tag. It will then bind an event to the dfn to catch mouseovers and position and show the dl parent of the coresponding dt. Anyway, thats the theory. Another way may be to just find all of the words in the pages that have definitions but, that could end up binding the wrong words in cases where there are multiple uses. Now if I were really smart, I would make this show language specific definitions. Making it keyboard accessible would be pretty sweet too.

To reduce bandwidth, the dl sections could be generated using some AJAX pixie dust, either on demand or in the background. Good use of header settings could exploit browser caching as well. The options are endless.

Enough talk, lets get started.

I'm going to start by creation some definitons for some words in this page, I've already marked them with dfn tags.

<dt lang="en">definition</dt>
<dd lang="en">A statement of the meaning of a word, phrase, or term, as in a dictionary entry.</dd>
<dd lang="en">The act or process of stating a precise meaning or significance; formulation of a meaning.</dd>
<dt lang="es">definición</dt>
<dd lang="es">Acción de definir una palabra o un concepto: hoy nos detendremos en la definición del concepto de relatividad.</dd>
<dd lang="es">Enunciado con que se define una palabra o un concepto: una palabra con tres definiciones.</dd>

Next, I'll add a little style:

<style type="text/css" media="all" title="default">
h1 {
font-size: 1.1em;
font-weight: bold;
dfn {
background-color: #eee;
cursor: default;
div.terms dl{
position: absolute;
background-color: #edf;
border: solid #00f 1px;
width: 20em;
font-size: 0.7em;
margin: 0;
padding: 0;

div.terms dl dt{
display: block;
float: left;
width: 100%;
margin-bottom: -1px;
background-color: #eee;
border-bottom: solid #00f 1px;
border-top: solid #00f 1px;
overflow: hidden;
div.terms dl dd{
float: left;
display: block;
border-top: solid #00f 1px;
margin: 0;
padding: 0;

And finally, some simple javascript to bring it to life:

String.prototype.trim = function() { return this.replace(/^\s+/,'').replace(/\s+$/,''); };
var TEXT_NODE = 3;
var ENTITY_NODE = 6;

function getNodeTextTrimed(node){
var text = "";
var children = node.childNodes;
for(var j=0; j<children.length;j++){
var child = children[j];
text += child.nodeValue;
return text.trim();
function getTagsByName(tagName){
var body = document.body;
/* lowercase for xhtml proper */
var elems = body.getElementsByTagName(tagName.toLowerCase());
/* Uppercase when html4 */
elems = body.getElementsByTagName(tagName.toUpperCase());
return elems;
* Find all of the dfn tags.
* returns a map using the text value as a key and an array of
* elements for the value.
function findDefs(){
var dfns = getTagsByName("dfn");
var DFNMAP = new Array();
for(var i=0;i<dfns.length;i++){
var curDfn = dfns[i];
var text = getNodeTextTrimed(curDfn);
DFNMAP[text] = new Array();
DFNMAP[text][DFNMAP[text].length] = curDfn;
return DFNMAP;
* Find all of the dt tags.
* returns a map using the text value for the key, and the parent dl
* element as the value.
function findDefinitions(){
/* lowercase for xhtml proper */
var terms = getTagsByName("dt");
var dlmap = new Array();
for(var i=0;i<terms.length;i++){
var cTerm = terms[i];
var text = getNodeTextTrimed(cTerm);
dlmap[text] = cTerm.parentNode;
return dlmap;
function showDef(){
var left = this.offsetLeft;
var top = this.offsetTop;
var height = this.offsetHeight;
var width = this.offsetWidth;
var defElem = this.def; = "block"; = (top + height+10) +"px"; = (left+width) + "px";
window.status=("top:"+ (top + height+10) +"px");
function hideDef(){
var defElem = this.def; = "none";
* Tie the dfn tags and the dl tags together
function bindDefs(){
var dfns = findDefs();
var dls = findDefinitions();
for( i in dfns ){
if(dfns[i]["length"] && dls[i]){
var elems = dfns[i];
for(var j=0; j<elems.length;j++){
elems[j].def = dls[i];
elems[j].onmouseover = showDef;
elems[j].onmouseout = hideDef;


A statement of the meaning of a word, phrase, or term, as in a dictionary entry.

The act or process of stating a precise meaning or significance; formulation of a meaning.


Acción de definir una palabra o un concepto: hoy nos detendremos en la definición del concepto de relatividad.

Enunciado con que se define una palabra o un concepto: una palabra con tres definiciones.

Note: The some of the definitions here were blatantly ripped off from and from

Links to this post