Summary

Although Internet Explorer recognises the q element, it doesn't render the quotations marks around the quoted text. A CSS solution isn't possible, as Internet Explorer doesn't support the :before and :after pseudo-elements, so this article suggests a scripting technique to fix the problem.

Author: Gez Lemon

Contents

Inline Quotations

The q element is intended for short inline quotations. According to the specification, the q element must be rendered with the appropriate quotation marks around the quote, and authors must refrain from adding quotation marks themselves. The reason for this is that the quotation marks should take into account the language, determined by the lang attribute of the nearest ancestor, or by the primary language of the document. Theoretically, this is particularly useful where several versions of the same document are served with content negotiation depending on the Accept-Language HTTP header, but I'm not sure how well-supported this is by browsers.

Nested Quotes

Not only do different languages use different types of quotation marks, but languages might also adopt different quotation styles for embedded quotes. For example, in English, quotations are marked up with either single or double quotes; whichever style is chosen for the main quote, the opposite is selected for the embedded quote. For example, both of the following are acceptable forms of nesting quotes in English:

Mary asked, "Did Pete say, 'Wait up.' or 'wake up.'?".

Mary asked, 'Did Pete say, "Wait up." or "wake up."?'.

Browser Support for Quotes

For English content, Opera uses double-quotes for all short inline quotations. Firefox also uses double-quotes for short inline quotation in English, but uses single quotes for quotations embedded in a quotation. Although Internet Explorer recognises the q element, it doesn't automatically render quotation marks. Unfortunately, Internet Explorer's lack of support for the :before and :after CSS pseudo-elements means that although the quotations are programmatically determinable, they can't be fixed with CSS. This has led to some developers tackling the problem from the other perspective; instead of adding quotes with CSS to Internet Explorer, they remove them from all browsers that support the q element, as these browsers support the :before and :after CSS pseudo-elements, and then physically put the quotes in the content — in complete contrast to the specification:

q:before, q:after
{
  content: "";
}

Fixing Inline Quotes with Scripting

As the q element is programmatically determinable, and it's desirable not to put the quotations directly in the markup, it makes more sense to use client-side scripting to fix the problem just for Internet Explorer. Taking this approach means that browsers that get it right continue to get it right, and Internet Explorer is made to work as best as it is able. The following script fixes the problem for Internet Explorer with typographical quotes using the rules that Firefox uses for English inline quotations:

function fixIEQuotes()
{
  var objQuotes = document.getElementsByTagName('q');
  var strOpen, strClose;

  for (var i=0; i<objQuotes.length; i++)
  {
    if (isNested(objQuotes[i]))
    {
      // Double-quotes
      strOpen = document.createTextNode('\u2018');
      strClose = document.createTextNode('\u2019');
    }
    else
    {
      // Single-quotes
      strOpen = document.createTextNode('\u201c');
      strClose = document.createTextNode('\u201d');
    }
    // Insert quotation marks around quote
    objQuotes[i].parentNode.insertBefore(strOpen, objQuotes[i]);
    objQuotes[i].appendChild(strClose);
  }

  objQuotes = null;
}

function isNested(objElement)
{
  var objParent = objElement;
  do // Check if nested quote
  {
    objParent = objParent.parentNode;
    if (objParent.tagName && objParent.tagName.toLowerCase() == 'q')
      return true;
  } while (objParent.parentNode);

  return false;
}

Browser detection is unavoidable in this case, as the script is intended only for Internet Explorer; one solution is to simply use Internet Explorer's conditional comment mechanism:

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

Update: Paul Davis has a great solution for fixing the q element using Internet Explorer's behaviors.

Category: Scripting.

Comments

  1. [fixing-ie-quotes.php#comment1]

    I would change the conditional comment to [if lte IE 7], because we don't know yet if IE8 will add the quotation marks or not.

    Posted by zcorpan on

  2. [fixing-ie-quotes.php#comment2]

    I would change the conditional comment to [if lte IE 7], because we don't know yet if IE8 will add the quotation marks or not.

    That's a good point, thank you - I've updated the conditional comment.

    Thanks,

    Posted by Gez on

  3. [fixing-ie-quotes.php#comment3]

    Outstanding Gez. I love all these solutions you come up with allowing developers to use the proper mark-up while at the same time taming IE's failings. Good job!

    Posted by Mike Cherim on

  4. [fixing-ie-quotes.php#comment5]

    Hi all, I couldn't get this to work. I linked the javascript file in the <head> section but IE still doesn't display the quotes. Am I missing anything here??
    Thanks in advance,
    Raj

    Posted by Raj on

  5. [fixing-ie-quotes.php#comment6]

    Am I missing anything here?

    The example just lists the function; you'll need to call it. For example, you could place the following at the very start of the JavaScript file:

    
    window.onload = fixIEQuotes;
    

    Posted by Gez on

  6. [fixing-ie-quotes.php#comment7]

    I've just written a similar script which hopefully deals with nested quotes in different languages.

    
    var qChars = new Array();
    qChars['en'] = new Array('&#8220;', '&#8221;', '&#8216;', '&#8217;');
    
    var defaultLang = 'en';
    
    
    function smartQuote() {
      var q = document.getElementsByTagName('q');
      var qLang = new Array(); // The language of each quote
      var qParentLang = new Array(); // Parent quote language
      var qLvl = new Array(); // Alternates between 0 and 1, depending on nesting level
      var qParentLvl = new Array();
      for (var i = 0; i < q.length; i++) { // Iterate through all quotes
        // Assign language to attribute value if exists (and quotes supported), otherwise parent language, otherwise default
        var lang = q[i].lang;
        qLang[i] = (lang && qChars[lang]) ? lang
                : (qParentLang[i]) ? qParentLang[i]
                : defaultLang;
        qLvl[i] = ((qParentLvl[i] == 0) && (qParentLang[i] == qLang[i])) ? 1 : 0;
        var innerQs = q[i].getElementsByTagName('q');
        for (var j = 0; j < innerQs.length; j++) { // Iterate through nested quotes
          qParentLang[i + j + 1] = qLang[i]; // Set parent language
          qParentLvl[i + j + 1] = qLvl[i]; // Set parent level
        }
      }
      for (var i = 0; i < q.length; i++) { // Apply all quotes depending on language and level
        var chars = qChars[qLang[i]];
        q[i].innerHTML = (qLvl[i] == 0) ? chars[0] + q[i].innerHTML + chars[1] : chars[2] + q[i].innerHTML + chars[3];
      }
    }
    
    window.attachEvent('onload', smartQuote);
    

    To use it, just save it as a separate file and use the IE conditional comments as shown above to import it for IE only. You may wish to add new languages; this is best demonstrated by example:

    
    // Norwegian
    qChars['no'] = new Array('&#171;', '&#187;', '&#39;', '&#39;');
    

    Add that line and you can use lang="no" in your HTML to indicate quotes in Norwegian. Unfortunately, you cannot just use the xml:lang attribute, although if you're willing to hack the script a little you could probably use a regex to extract the language.

    Hopefully someone will find this useful!

    Posted by Jordan Gray on

  7. [fixing-ie-quotes.php#comment8]

    An apology is due. The function I posted above was written on the assumption that nested quotes should alternate (which is the most common technique in English), when in fact, it turns out that the W3C specifies that nested quotes should repeat after the last level of quoting specified.

    As pennance I rewrote the function to more closely follow the W3C specification. I also extended it so that it could support additional levels of quotes, or even missing out the last quotation mark. (Some languages, eg. Finnish, allow one to use a lone emdash to start a quote, a technique also used in dialogue.)

    function smartQuote() {
      var q = document.getElementsByTagName('q');
      var qLang = new Array(); // The language of each quote
      var qParentLang = new Array(); // Parent quote language
      var qLvl = new Array(); // Incremented by one for each level of nesting
      var qParentLvl = new Array();
      for (var i = 0; i < q.length; i++) { // Iterate through all quotes
        // Assign language to attribute value if exists (and quotes supported), otherwise parent language, otherwise default
        var lang = q[i].lang;
        qLang[i] = (lang && qChars[lang]) ? lang
                : (qParentLang[i]) ? qParentLang[i]
                : defaultLang;
        qLvl[i] = (qParentLvl[i] >= 0) ? qParentLvl[i] + 1 : 0;
        var innerQs = q[i].getElementsByTagName('q');
        for (var j = 0; j < innerQs.length; j++) { // Iterate through nested quotes
          qParentLang[i + j + 1] = qLang[i]; // Set parent language
          qParentLvl[i + j + 1] = qLvl[i]; // Set parent level
        }
      }
      for (var i = 0; i < q.length; i++) {
        var chars = qChars[qLang[i]];
    	var charLevel = (qLvl[i] < chars.length / 2) ? qLvl[i] * 2 : (chars.length % 2 == 0) ? chars.length - 2 : chars.length - 1;
        q[i].innerHTML = chars[charLevel] + q[i].innerHTML + (chars[charLevel + 1] ? chars[charLevel + 1] : '');
      }
    }
    

    Also, to make good my atonement (and because I really do like alternating nested quotes), I've extended it to work even more like the CSS 'quotes' property. You can now add additional levels of quote for any language (eg. Spanish, which has three) simply by adding extra pairs of opening and closing quotes to the array. Here's an example:

    // Adding two extra levels of alternating quotes in English
    qChars['en'] = new Array('\u201c', '\u201d', '\u2018', '\u2019', '\u201c', '\u201d', '\u2018', '\u2019');
    

    And, of course, you can define additional languages by adding new quotation entity arrays.

    Quick comparison:

    - in terms of efficiency our scripts don't differ significantly;
    - as for size, mine is a little larger (about 200b with comments stripped out), which is probably not a concern if you're using broadband;
    - from a stylistic perspective, his script uses beautiful recursion and proper DOM methods, so you're less likely to go to Hell for using his script;
    - however, it makes up for infernal damnation by allowing you to use HTML entities as well as unicode (come on; this _is_ a hack, after all).

    In summary, there's not much to chose between the scripts unless you need multi-language support or additional levels of quotes.

    Another limitation of my script is that it doesn't look above the quotes to check if the lang attribute is assigned somewhere else; again, you need to use the lang="xx" attribute if you want something other than the default language (which you can change at the top of the script).

    Further, a "missing" closing quote only counts for the last level of quotes; however, you can simulate it further up the chain by simply substituting '' for one of the characters.

    My word, I hope that was coherent.

    Posted by Jordan Gray on

  8. [fixing-ie-quotes.php#comment9]

    The example just lists the function; you'll need to call it.

    Thanks a lot, Gez. Also, is there any method where it still works even though JavaScript is turned off? Or am I asking too much?

    Posted by Raj on

  9. [fixing-ie-quotes.php#comment10]

    ...is there any method where it still works even though JavaScript is turned off? Or am I asking too much?

    Sadly, this is no alternative on the client-side. The only real way to do this would be to use browser-sniffing to trigger some clever server-side scripting which would insert actual quotes in place of <q> elements.

    In my opinion, it would not be worth the effort to support this userbase. IE without JScript is a very odd configuration, since it cripples the browser and is quite counter-intuitive to set up. If it is done for security reasons (which it generally is, since the average user doesn't know how to do it) the practice should be discouraged in favour of downloading non-targetted browsers, such as Opera or Firefox.

    Posted by Jordan Gray on

  10. [fixing-ie-quotes.php#comment11]

    Thank you loads Jordan, for the explanation and the prompt reply. *smile*

    Posted by Raj on

  11. [fixing-ie-quotes.php#comment12]

    Hello,

    This not working at my end. Should I replace q with my element?

    I am unable to fix this.

    Posted by Azizi on

  12. [fixing-ie-quotes.php#comment13]

    Azizi,

    Although you have the conditional comments for IE, you haven't uploaded the ie.js file to your server, and you don't have any quotes in your content. You only require this fix if you use quotations:

    
    <p>
    Dave said,
    <q>
    The weather is particularly bad today.
    </q>
    </p>
    

    Posted by Gez on

  13. [fixing-ie-quotes.php#comment14]

    Thanks Gez,

    It's true that I am not using <q>, but I am here to fix :before and :after pseudo-elements.

    I am using them in both my left and top menu:

    
    #smenu:before {
      line-height: 0.1;
      font-size: 1px;
      background: transparent url("../images/menu_tr.gif") no-repeat top right;
      margin: 0;
      height: 9px;
      display: block;
      border-bottom: 1px solid #ddd;
      content: url("../images/key-point_tl.gif");
    }
    

    Where "smenu" is selector that's applied to a div. Now please just guide me to fix this.

    Posted by Azizi on

  14. [fixing-ie-quotes.php#comment15]

    This is a topic we discussed at CSSCreator (http://www.csscreator.com/node/17258 and http://garyblue.port5.com/webdev/quotes.html ). We more or less settled on there being no good way to deal with it without going counter to the specs.

    I think going at it with scripting has a certain elegant appeal, but fails should javascript fail. Further, if you're going to buck the standards, doing it in the background doesn't make the approach any more valid. The script route leaves only Lynx and like browsers with valid markup, and sans added quote-marks.

    The importance of the quote marks to readability, and the importance of the q tag to assistive technology says to me that failure is not an option. The less elegant manual quote-mark entry and css to null the proper defaults is more robust. Only Lynx, et al., have the problem of doubled quote-marks.

    Posted by Gary Turner on

  15. [fixing-ie-quotes.php#comment16]

    The "quotes reset via content property" approach seems to be quite popular, but usually (though still a feature at risk), you would just use the following:

    q { quotes: '' '' '' ''; }

    Posted by Jens Meiert on

Comments are closed for this entry.