O'Reilly logo

JavaScript Cookbook by Shelley Powers

Stay ahead with the world's most comprehensive technology and business learning platform.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, tutorials, and more.

Start Free Trial

No credit card required

11.6. Highlighting the First Paragraph in Every Element

Problem

Based on some user event, you want to dynamically change the background color to yellow for the first paragraph in every div element.

Solution

Use the document.querySelectAll with the appropriate CSS selector in order to reference all first paragraphs in div elements, and then modify the paragraph’s CSS background color:

var paras = document.querySelectorAll('div p:first-of-type');
for (var i = 0; i < paras.length; i++) {
    paras[i].setAttribute("style","background-color: #ffff00");
}

If the specific pseudoselector syntax is not supported, use an alternative, such as the following:

var divs = document.querySelectorAll("div");
for (var j = 0; j < divs.length; j++) {
   var ps = divs.item(j).getElementsByTagName("p");
   if (ps.length > 0) {
       ps[0].setAttribute("style","background-color: #ffff00");
   }
}

Discussion

We’re only interested in selectors where the paragraph element is a descendant of a div element:

var paras = document.querySelectorAll('div p');

In addition, we’re interested in the first paragraph element in the div, so at first glance, the following looks acceptable:

var paras = document.querySelectorAll('div p:first-child');

However, there’s no guarantee that the div element won’t contain elements of other types, and if the first element is not a paragraph, the first paragraph won’t be found. Instead, as shown in Example 11-3, the :first-of-type CSS selector is used so that the first paragraph in the div element is highlighted when the document is clicked, even if it isn’t the first element in the div. The code is included in a try...catch block in order to provide an alternative if the type of selector syntax isn’t supported.

Example 11-3. Using the first-of-type selector in order to highlight the first paragraph element in a div

<!DOCTYPE html>
<head>
<title>paras</title>
<meta charset="utf-8" />
<style>
div
{
  padding: 10px;
  border: 1px solid #000000;
}
</style>
<script type="text/javascript">

window.onload=function() {
   document.onclick=function() {
     try {
      var paras = document.querySelectorAll('div p:first-of-type');
      for (var i = 0; i < paras.length; i++) {
         paras[i].setAttribute("style","background-color: #ffff00");
      }
     } catch(e) {
       var divs = document.querySelectorAll("div");
       for (var j = 0; j < divs.length; j++) {
          var ps = divs.item(j).getElementsByTagName("p");
          if (ps.length > 0) {
             ps[0].setAttribute("style","background-color: #ffff00");
          }
        }
     }
   };
}
</script>
</head>
<body>
  <div>
     <p>Paragraph one</p>
     <p>Paragraph two</p>
     <p>Paragraph three</p>
  </div>
  <div>
     <p>Paragraph one</p>
     <p>Paragraph two</p>
  </div>
  <div>
     <ul>
        <li>List item one</li>
        <li>List item two</li>
     </ul>
     <p>Paragraph one</p>
     <p>Paragraph two</p>
  </div>
</body>

Figure 11-3 shows that even though the first element in the third div is an unordered list, the first paragraph that follows is still highlighted. Another CSS selector that provides the same functionality is :nth-of-type(1), where parentheses are used to wrap the number of the target element.

Page displaying highlighted first paragraphs in every div element

Figure 11-3. Page displaying highlighted first paragraphs in every div element

Firefox, Safari, Chrome, and Opera support :first-of-type. IE8 doesn’t, but it does support :first-child. However, as the example demonstrates, we can’t count on the paragraph being the first element. Instead, we use a more generalized query for all div elements, and then access all the paragraphs with getElementsByTagName.

We could use the getElementsByTagName for the first query, except that this method returns a live collection, and the first approach doesn’t. We want the functionality to be the same for both approaches, as much as possible. If you need to support IE7, though, you should use getElementsByTagName, as this browser doesn’t support querySelectorAll.

See Also

See Recipe 11.2 for more on getElementsByTagName and live collections, and Recipe 11.4 for an in-depth introduction to the Selectors API. Microsoft provides a page for the CSS selectors it supports at http://msdn.microsoft.com/en-us/library/cc351024(VS.85).aspx. Note, though, that Microsoft does have a habit of changing its URLs, so this web page address may not work in the future.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, interactive tutorials, and more.

Start Free Trial

No credit card required