Pressure is nothing more than the shadow of great opportunity. - Michael Johnson

Correcting the Quote (q) Element in IE

8 years, 6 days ago

Introduction

28th October 2006: I've created an updated example of the code described here which more closely follows the CSS 2 specifications for how nested quotes should be displayed.

I recently had a go at creating a JavaScript routine to correct the display of the quote element in Internet Explorer. Interestingly Safari and Opera don't get things totally correct either. The W3C recommendations specify that many languages adopt different quotation styles for outer and inner (nested) quotations, which should be respected by user-agents however they render double quotes in both cases.

I was going to write a detailed post about quotations but I then discovered Gez Simon's article which was close to what I was planning so I thought it would be simpler to just link to his instead. I've included my code below anyway in case it's useful to anyone:

JavaScript Code

var oLang = {
   "en-us" : {
      "left" : ["\u201C", "\u2018"],
      "right" : ["\u201D", "\u2019"]
   },
   "default" : "en-us"
}
var oQuotations = {
   init : function() {
      if (!document.getElementsByTagName) {
         return false;
      }
      var aQuotations = document.getElementsByTagName('q');
      for (var i = 0, j = aQuotations.length; i < j; i++) {
         var iQuoteType = 0;
         var sLang = oQuotations.getLang(aQuotations[i]);
         if (oQuotations.isInA(aQuotations[i], 'q')) {
            iQuoteType = 1;
         }
         aQuotations[i].insertBefore(document.createTextNode(oLang[sLang]["left"][iQuoteType]), aQuotations[i].firstChild);
         aQuotations[i].appendChild(document.createTextNode(oLang[sLang]["right"][iQuoteType]));
      }
   },
   getLang : function(oElement) {
      var sLang = oElement.getAttribute('lang');
      if (sLang == "") {
         return oLang['default'];
      }
      return sLang;
   },
   isInA : function(oElement, sParent) {
      var oParent = oElement;			
      while (oParent = oParent.parentNode) {
         if (oParent.nodeName.toLowerCase() == sParent) {
            return true;
         }
      }
      return false;
   }
}
YAHOO.util.Event.addListener(window, 'load', oQuotations.init);

You can view a working example here.

It's split into two object literals. The second handles the logic whilst the first contains configuration information specifying which quote pairs to insert for different languages. Language selection takes place based on the lang attribute of the quote. The default configuration specifies that double quotes should be inserted around top level quotes whilst single quotes should be used for direct and indirect children.

Detecting Internet Explorer

The code is selectively served to Internet Explorer 7 and below via conditional comments in the HTML.

<!--[if lte IE 7]>
   <script type="text/javascript" src="js/quotations.js"></script>
<![endif]-->

isInA Function

The isInA function is used to determine if a particular quote element is nested. It could be used more generically to determine if any element is a direct or indirect child of any other type of element.

function isInA(oElement, sParent) {
   var oParent = oElement;			
   while (oParent = oParent.parentNode) {
      if (oParent.nodeName.toLowerCase() == sParent) {
         return true;
      }
   }
   return false;
}

Unicode

The quote characters are specified in unicode. You can use the character lookup table (UTF-8) on MauveCloud's site to choose alternatives.

Other Methods

A little searching reveals a number of solutions.

Further Reading

Comments

  • Nested q elements alternate between double and single quotes so a double nested element should be double quotes. Perhaps you could count the number of parent q elements and select the correct type of quote so it works on elements nested to any depth.

    function isInA(oElement, sParent) {
       var oCount = 0;
       var oParent = oElement;			
       while (oParent = oParent.parentNode) {
          if (oParent.nodeName.toLowerCase() == sParent) {
             oCount += 1;
          }
       }
       return oCount;
    }

    iQuoteType = oQuotations.isInA(aQuotations[i], 'q') % 2;

    Kanashii - 26th October 2006 #

  • Kanashii - I've been wondering about this point. I've seen conflicting information on what the behaviour should be. This comment suggests that quotes shouldn't alternate but should instead repeat after the last stated value. I didn't think the W3C recommendations really made it clear.

    I'm also curious as to how many levels of nesting are likely to be used in real world examples.

    Perhaps allowing specification of different quotes for further levels is a good idea. Your sample code should help with this - thanks for posting.

    Ed Eliot - 26th October 2006 #

  • Ah very interesting. So if there were 3 types of quotes it would use first, second, third, first, second.. if it ever got that deep? Or am I still interpreting wrong? I'm confused : )

    How about using the length of the array to set the mod so: iQuoteType = oQuotations.isInA(aQuotations[i], 'q') % oLang[aLang]['left'].length;

    Kanashii - 26th October 2006 #

  • Kanashii - yeah, it does seem very confusing. I spent ages searching around and eventually found this in the CSS 2 specifications: If the depth is 0, the first pair is used, if the depth is 1, the second pair is used, etc. If the depth is greater than the number of pairs, the last pair is repeated.

    You can read the full CSS 2 specifications for quotations here

    Therefore one can specify an unlimited number of quote pairs but once the last pair is reached only that pair should be repeated for each subsequent nested quote.

    I've created a new version of my code which supports this functionality and also altered the quotes array structure to match the way you'd specify it in CSS.

    View Example 2

    Ed Eliot - 28th October 2006 #

  • I can't get your script, or Gez Simon's working. It seems to be something with the document.getElementsByTagName('q') object. When I output the length of the object, it always comes out as zero, so the for loop never runs and nothing happens.

    Any ideas would be greatly appreciated (perhaps something Wordpress or XML lang related?). I've been pulling my hair out over this.

    Jeff - 12th December 2006 #

  • Jeff - Seems weird. Do you have an example script I can look at?

    Ed Eliot - 13th December 2006 #

  • Here's the script I'm using:

    http://www.equivocality.com/js/fixIEQuotes.txt

    This will output "Function running, quotes object length: 0", and that's it, the for loop won't run. I test on a page such as this, which has a few q tags in it:

    http://www.equivocality.com/2006/11/06/rebel-son/

    Thanks for taking a look, Ed!

    Jeff - 13th December 2006 #

  • Any luck? I'm about to give up and just switch to using hard-coded quotes instead of the q tag. I hear XHTML 2 is switching it to a "quote" tag instead, and until things are settled, I'll go with the method that works in every browser.

    Jeff - 14th December 2006 #

  • Jeff: If you use document.write before you try to access the DOM, it won't work. Since you're running the script after the page has loaded, document.write will start a new document and your DOM tree will be lost. Your method of debugging is the culprit. :-)

    Ed: This is nice work. I'm also puzzled by the guidance of the CSS2 spec -- in English, at least, you'd alternate between single and double quotes ad infinitum, but maybe that's not the case in other languages.

    A quick note: as Gez Simon points out, the proper behavior is to pluck the lang attribute of the Q tag or that of its nearest ancestor, going all the way up to the document's language if need be.

    So you could write a more robust getLang function that travels up parent nodes until it finds a lang attribute. If there isn't one specified, the script could try grabbing the user's locale (via navigator.systemLanguage) before it falls back to the default. But that's a whole other can of worms.

    At any rate, this is the first solution I've seen that accomodates non-English locales, so this is good work.

    Andrew Dupont - 14th December 2006 #

  • Thanks for the suggestion Andrew. Unfortunately, this is starting to go over my head, and I don't understand what to do to fix the problem. Would you have an e-mail I can contact?

    I don't want to flood Ed's post with comments.

    Jeff - 15th December 2006 #

  • Andrew - Thanks for your comments and for chipping in to try and help solve Jeff's problem.

    Jeff - If comments posted to this blog help to solve a problem that's great - post away.

    The document.write lines aren't actually causing the problem. The following line is:

    window.onload = fixIEQuotes();

    By adding brackets after fixIEQuotes you're telling IE to run the function straight away and assign the result to window.onload rather than specifying that the function should be run onload. It should read:

    window.onload = fixIEQuotes;

    Ed Eliot - 15th December 2006 #

  • I made a fix for this by using behaviors in IE. You can view the full source (with explanation) on my site: http://willcode4beer.com/tips.jsp?set=fixIEQuotes

    I am also able to cope with various levels of nested tags. I hope its useful

    Paul Davis - 7th February 2007 #

Help make this post better

Notes: Standard BBCode for links, bold, italic and code are supported. rel="nofollow" is added to all links. Your email address will never be displayed on the site.

Back to index