Blockquote/Quote Detail Hover

On my little used html element page, I present an idea (and demonstration) for hovering citation detail blocks for BLOCKQUOTE and Q elements. Here, I'll show the code that makes it happen and hopefully explain it well enough that it can be re-used or modified.

A little bonus, it also demonstrates graphic free rounded corners (without making a billion little divs).

Demo

The following blockquote provides a little demo of the concept (just mouseover the blockquote).

I have a dream for the Web [in which computers] become capable of analyzing all the data on the Web the content, links, and transactions between people and computers. A 'Semantic Web', which should make this possible, has yet to emerge, but when it does, the day-to-day mechanisms of trade, bureaucracy and our daily lives will be handled by machines talking to machines. The 'intelligent agents' people have touted for ages will finally materialize.

- Tim Berners-Lee

Components

The main components of this are, the CSS for the look and feel, and the javascript to make it all happen. The HTML is actually very basic and straightforward. All of the parts may be downloaded in the resources section.

The HTML

The HTML elements BLOCKQUOTE and Q, are very similar. The main difference being that BLOCKQUOTE is used for large pieces that are to be separated a little, and Q is used for inline quotes. The similarities are what we care about, the attributes, CITE and TITLE will be used to populate the hovering detail block.

The main concept with the HTML component is that it is not important. What? Ok, it is important. But, the HTML should only have the normal semantic information that you should be using anyway. The script makes use of two attributes, cite and title (which should be used anyway). As described above, the title will be used for the header of the hovering block, and the value of site will be used to create a hyperlink.

The CSS (Cascading Style Sheet)

I'm not going to spend any time on the CSS. About the only thing it does is allow for squishing paragraph elements inside of blockquotes and defines the look of the hovering blocks. The hovering blocks will consist of a parent div (or v:roundrect for IE). Inside of this will be a span element containing the title text, and a div which will contain the link.

The JavaScript

The javascript is broken up into four objects. The first (BlockQTipsConf) contains configuration information. The second, BlockQTips, is used to define the hovering blocks. Next, BlockQuoteEvents contains the event handlers. And finally, Effects, just contains a little fading effect.

Configuration (BlockQTipsConf)

  1. var BlockQTipsConf = {
  2.   offsetLeft : (-25)      /* pixel offset */,
  3.   offsetTop  : (-20)      /* pixel offset */,
  4.   width      : 20         /* width in em's */,
  5.   opacity    : "0.85"     /* final opacity of the block */,
  6.   borderColor: "#555555",
  7.   borderWidth: "1px",
  8.   bgColor    : "#bbbbbb"
  9. };

This object provides the configuration information for the hovering citations. The offsetLeft and offsetTop properties, indicate where the citation should appear relative to the cursor. The opacity property indicates the final opacity of the element after it has finished fading in. The rest of the properties should be pretty self explanatory.

Create the Citation Objects (BlockQTips)

The method to create the citation objects is with a call to the activateOn function in the BlockQTips object. The parameter passed is simply the name of the tags that the effect should be applied to.

  1. <script type="text/javascript">
  2.   BlockQTips.activateOn("blockquote");
  3.   BlockQTips.activateOn("q");
  4. </script>

Next up, the code for the BlockQTips object.

  1. var BlockQTips = {
  2.   activateOn : function(type){
  3.     var tags = document.getElementsByTagName(type);
  4.     for(var i=0;i<tags.length;i++){
  5.       this.buildCitation(tags[i]);
  6.     }
  7.   },
  8.   buildCitation : function(elem) {
  9.     var titleText = elem.getAttribute("title");
  10.     var url = elem.getAttribute("cite");
  11.     elem.removeAttribute("title");
  12.     if(titleText==null || titleText.length==0){
  13.       titleText = "No Info";
  14.     }
  15.     var citation = document.createElement("div");
  16.     var vmlWrapper = document.createElement("v:roundrect");
  17.       vmlWrapper.strokecolor  = BlockQTipsConf.borderColor;
  18.       vmlWrapper.strokeweight = BlockQTipsConf.borderWidth;
  19.       vmlWrapper.fillColor    = BlockQTipsConf.bgColor;
  20.       vmlWrapper.arcSize = "15%";
  21.     if(typeof vmlWrapper.style.behavior != "undefined"){
  22.       /* Cheap hack to determine if client has VML */
  23.       citation = vmlWrapper;
  24.     }
  25.     citation.className = "citation";
  26.     citation.style.width = BlockQTipsConf.width + "em";
  27.     citation.style.position = "absolute";
  28.  
  29.     var title = this.createElem("span","citeTitle");
  30.     title.appendChild(document.createTextNode(titleText));    
  31.     var citeAnchorSec = this.createElem("div","citeBottom");
  32.     var anchor = document.createElement("a");
  33.     anchor.href = url;
  34.     var urlSize = (BlockQTipsConf.width * 1.5);
  35.     var urlText = this.makeUrlFit(url,urlSize);
  36.     anchor.appendChild(document.createTextNode(urlText));
  37.     anchor.style.fontFamily = "monospace";
  38.     citeAnchorSec.appendChild(anchor);
  39.     citation.appendChild(title);
  40.     citation.appendChild(citeAnchorSec);
  41.  
  42.     elem.citation = citation.citation = citation;
  43.     elem.onmouseover = citation.onmouseover = BlockQuoteEvents.showCitation;
  44.     elem.onmouseout  = citation.onmouseout  = BlockQuoteEvents.hideCitation;
  45.     document.getElementsByTagName("body")[0].appendChild(citation);
  46.   },
  47.   createElem : function(tag,className){
  48.     var elem = document.createElement(tag);
  49.     elem.className = className;
  50.     elem.style.display = "block";
  51.     return elem;
  52.   },
  53.   makeUrlFit : function(url,size){
  54.     var urlText = url;
  55.     size -=4;
  56.     if(url.length>size){
  57.       urlText =
  58.         urlText.substring(0,size/2)
  59.         + "....."
  60.         + urlText.substring(urlText.length-size/2);
  61.     }
  62.     return urlText;
  63.   }
  64. };

This object defines four functions: activateOn, buildCitation, createElem, and makeUrlFit. As mentioned before, the method activateOn (line 106) is used to connect the citations to a specified type of tag. You can see that it simply loops (lines 109-111) through all of the tags, and binds the citation objects to them.

The function buildCitation (line 113), creates the citation object as either a div (line 120) or as a VML rounded rectangle (line 121). The VML rounded rectangle is used on Internet Explorer since it does not recognize a CSS attribute for rounded corners. The text is pulled out of the title attribute (line 114) and the title attribute is removed (line 116), to prevent the default tooltip from showing. The URL is pulled from the CITE attribute (line 115). Next, the citation object is built up (lines 130-145) by creating the various nodes for the txt. Lastly, references are set for both the original element (being bound to) and the citation object (line 147). This allows the citation object to be referenced on the events, that are bound in lines 148 and 149. And the citation object is added to the end of the page (line 150) to facilitate positioning.

The createElem function (line 152) is used to create block tags with a specified css class.

The makeUrlFit function (line 158) called earlier (line 140) is used to shorten the size of URLs that are too long by placing a few dots in the middle.

Event Handling (BlockQuoteEvents)

The BlockQuoteEvents object contains the logic needed for handling the mouse events. This allows the display, positioning, and removal of the hovering citation element. You may have noticed the dual referencing for both the element getting the citation and the citation itself buildCitation function shown above. This is required so that we do not hide the citation element when the user moves their mouse onto the citation element. By applying the event to it, the mouse out from the original block can be over-ridden.

  1. var BlockQuoteEvents = {
  2.   posRef : function(){
  3.     return (
  4.       (document.documentElement.scrollTop)?
  5.         document.documentElement : document.body
  6.       );
  7.   },
  8.   showCitation : function(e){
  9.     var citation = this.citation;
  10.     if(window.activeCit == citation){
  11.       clearTimeout(window.hideEvent);
  12.     }
  13.     if(citation.style.display != "block"){
  14.       citation.style.display = "block";
  15.       Effects.fadeIn(citation,BlockQTipsConf.opacity);
  16.       if(!e){
  17.         e = window.event;
  18.       }
  19.       BlockQuoteEvents.moveCitation(e,citation);
  20.     }
  21.   },
  22.   moveCitation : function(e,citation){
  23.     if(e == null){ e = window.event };
  24.     var posx = BlockQTipsConf.offsetLeft;
  25.     var posy = BlockQTipsConf.offsetTop;
  26.     if(e.pageX || e.pageY){
  27.       posx += e.pageX;
  28.       posy += e.pageY;
  29.     } else if(e.clientX || e.clientY) {
  30.       posx += e.clientX + BlockQuoteEvents.posRef().scrollLeft;
  31.       posy += e.clientY + BlockQuoteEvents.posRef().scrollTop;
  32.     }
  33.     citation.style.top  = (posy) + "px";
  34.     citation.style.left = (posx) + "px";
  35.   },
  36.   hideCitation : function(e){
  37.     var citation = this.citation;
  38.     window.activeCit = citation;
  39.     window.hideEvent = setTimeout( function(){
  40.       window.activeCit.style.display = "none";
  41.       }, 200);
  42.   },
  43.   _cleanup : function(){
  44.   }
  45. };

The main components of this object are: showCitation, hideCitation, and moveCitation. The function posRef is used to get a browser specific position reference. This is used in the moveCitation function to accurately position the citation object relative to the cursor.

The showCitation function (line 65) is called on mouseover of the element a citation was bound to, as well as the citation itself. It first checks to see if the same element is being hidden (lines 67-69) and stops that. If the element is not already being shown (line 70), then the citation object is shown (line 71), the fade-in effect is started (line 72), and the element is positioned (line 76) relative to the mouse.

The moveCitation function (line 79) is used to position the citation object relative to the cursor. It uses the configuration data (lines 87-88) to determine the offsets from the pointer.

Lastly, the hideCitation function (line 93), is called when the mouse moves away from the object with the citation. This simply hides the object on a delay (lines 96-98). References are set for the object and the timeout (lines 95-96) to allow canceling. This gives the user time to move their pointer from a blockquote item to the hovering citation object.

Effects

This uses the same fading effect as the bubble tooltips. The only difference, in this case, we are only using the fade in effect.

Conclusion

This should have provided an explanation of a way to add a hovering detail element to a simple HTML element without the need to add any extra HTML elements that would violate the semantics of the page. Since the data is additive (and set at runtime) the original meaning of the markup is unchanged. By exploiting the natural use of some attributes, hopefully, we can enhance the user experience (or completely annoy them thoroughly with flashy thingies).

Good luck with the usage, and if anything is unclear, or you just have questions, comments, or insults, feel free to contact me at: feedback@willcode4beer.com.

Resources



Sponsors:

About willCode4Beer