Wednesday, September 20, 2017

CSS Selectors - Level 1 & 2

As my interest in CSS grew, I have come across more CSS selectors that I should have used in my projects, but haven't due to ignorance. I wrote this blog post more as a way to freeze these selectors in my brain so that I remember to use them next time. To keep things simple, I make two assumptions for this post:
  1. We're trying to use CSS for HTML, and
  2. We're only using CSS selectors until CSS Selectors level 3. Level 4 is not yet a W3C Recommendation, so when that happens, I shall have a new blog post.
Also, this blog post talks only about selectors in CSS 1 & 2. Selectors in CSS 3 will be looked at in another blog post.

Level 1 CSS selectors


When CSS was first announced, it was called Level 1 CSS. It had only a few selectors. (Later "versions" of CSS added more selectors, as demands from web designers grew.) Because CSS 1 is quite old, it's also extremely well-known among developers & designers. Thus, for the most popular selectors in CSS 1, I'll skim over them as most developers/designers know these by now.

Type selector

This selector only contains the name of an HTML tag. All elements on the page of that type will be applied the associated style.
An example: span {color:red}

This will make all text inside <span> tags red in colour.

ID selector

The ID selector contains the name of an HTML element's ID attribute. Only the element that has that particular value for the ID attribute will have the style applied to it. The ID selector is constructed by having the hash (#) symbol followed by the HTML element's ID without any space.

Eg: #content {color:red} will render the HTML element with ID 'content' in red coloured text.

Class selector

The class selector defines a style that can be applied to a class (aka. category) of HTML elements. What determines if an element belongs to the class or not is the class attribute of the HTML element. If the class attribute's value matches the class selector, then the style is applied.

As an example, consider two span tags,
<span id="span1">Content</span>
<span id="span2" class="important_content">Warning message</span>

and a style definition,
.important_content {color: red;}

In the above case, only the <span> tag with id 'span2' will have the content in red colour. This is because it is the only <span> tag that belongs to the class of elements represented by 'important_content'. Other <span> tags in the same document do not belong to that class of elements and hence will not have the content in red colour.

Because this selector is used to categorize elements into a single grouping, you can have multiple elements having the same value for the class attribute - all those elements will be grouped into a single category represented by that class selector, and the style will be applied for all those elements.

Descendant selector

The descendant selector is used to select any element that is a descendant of any other element.

The syntax is ancestor targeted_element. The style is applied to targeted_element.

Here is an example. Consider you have the following HTML:
<div>
    <span>Span tag inside a div tag</span>
    <ul>
        <li>
            <span>This is the first list item.</span>
        </li>
        <li> This is the second list item.</li>
    </ul>
</div>
with this CSS applied:
div span {color:blue;}
The output we get is:

What's happening here is that all <span> tags at all levels of the <div>'s subtree get the style applied.

Pseudo-classes

CSS 1 had 3 pseudo-classes: :link, :visited, and :active. These work only on links and therefore, defining them on other elements has no effect. The :link pseudo-class is to specify how links will normally be shown, the :visited pseudo-class is to specify how those links on the page which point to URLs you have already visited are to be shown, and the :active pseudo-class is to specify how a link should appear when it is currently being selected.

Pseudo-elements

CSS 1 had 2 pseudo-elements: :first-letter & :first-line.
Note that in CSS 3, the single colon (:) at the beginning of each pseudo-element has been replaced by the double colon (::). Thus, if you're trying out these examples on a modern browser, remember to use double colons.For convenience, my example code already has them replaced.

The :first-letter pseudo-element

The :first-letter pseudo-element is used to specify a styling only for the first-letter of the element's content. Sometimes, it is to indicate that a new paragraph has started; other times it is for publishing scenarios such as drop caps.
As an example, let us assume that you have the following HTML content:
<div id=first_div>
    This is a single line of text.
</div>
<div id=second_div>76 is a number.</div>
<div id=third_div>
    "Let's go", he said, with determination.
</div>
and the following CSS is applied to the HTML:
#first_div::first-letter {font-size: 20pt;}
#second_div::first-letter {font-size: 20pt;}
#third_div::first-letter {font-size: 20pt;}
The result is this:
What's happening here is that the first letter of each <div> tag is being applied the style to increase its font size. We are able to do this without wrapping the first letter in any special tag (eg: a <span> tag). If not for the :first-letter pseudo-element, wrapping the first letter in a special tag would be the only way to achieve the same effect.

This is also the reason why :first-letter is a pseudo-element. The CSS selector applies the style as if there was a tag wrapped around the first letter with that special styling.

What is also noticeable is that if the content of the element starts with any quotes, then the :first-letter style applies to the quotes and the letter following it.

The :first-line pseudo-element

The :first-line pseudo-element is used to specify a styling only for the first line of the element's content. This is often required in publishing scenarios where publishers may prefer to highlight the first line of a new paragraph to make it easier to identify that a new paragraph is starting here.
As an example, let us assume that you have the following HTML content:
<div>
    They walked into the forest, not entirely unmindful of the animals that lurked there. But to them, much more than the dangers the animals represented, was the fear of losing out - of not achieving their goal.
</div>
and the following CSS is applied to the HTML:
div::first-line {text-transform:uppercase;}
The result is this:
What happened here is that only the first line of the <div> tag is in upper-case. Notice that we mean first line & not first sentence, i.e., CSS does not look for a full-stop (aka period) to indicate the end of the first sentence. It only applies the style until the content in the <div> tag wraps to the next line, at which point the style application stops.

If you resize your browser window to make the line longer or shorter, more (or less) content will be made upper-case.

Level 2 CSS selectors

Let us now look at what was newly introduced in CSS 2.

Universal selector

The universal selector represents all elements in an HTML document. The universal selector can only be written using the asterisk (*) symbol.
An example: *{color:red}
This will make all text red in colour.

Attribute selectors

CSS 2 introduces the concept of attribute selectors, where you can specify that your styles must match only those elements that have certain attributes or characteristics of attributes. CSS 2 introduces 4 attribute selectors (CSS 3 adds 3 more). Let's take a look at the 4 attribute selectors below:

Presence selector

This selector matches those elements that have the attribute specified. The attribute only has to be present in the element - it can have any value or it can have no value.

The syntax of the selector is element[attribute].

As before, here's an example. Let's assume you have the following HTML code:
<div id=first_para>
    <span>This is the first paragraph.</span>
</div>
<div id=second_para>
    <span>And this is the second.</span>
</div>
and the following CSS:
div[id] {color:blue;}
The result is this:
What happened here is that the blue colour styling was applied to both <div> tags, even though they have different HTML IDs. That is because the selector only checks for the presence of the attribute - it doesn't check for the presence of a value.

Where would such selectors be used? In HTML, there are places where an attribute just has to exist for some behaviour to be triggered - the attribute doesn't have values as per the HTML spec. An example is the selected attribute for checkboxes & list boxes.

Value selector

This selector matches those elements that have the attribute specified, and that attribute's value is the value specified in the selector. Note that this selector is case-sensitive - it will match the element only if the value of the attribute is the same case as the one specified in the selector.

The syntax of the selector is element[attribute=value].

Let's assume you have the following HTML:
<div id=first_para>
    <span>This is the first paragraph.</span>
</div>
<div id=First_para>
    <span>This is the second paragraph, but has the same ID, different only in case.</span>
</div>
and this CSS:
div[id=first_para] {color:blue;}
The output we get is this:
What happened here is that only the <div> tag with the ID attribute having first_para as its value got the style applied. Other <div> tags did not get that style because they didn't have the exact value with the exact case as in the selector.

Attribute sub-string selectors

There are 2 attribute selectors available in CSS 2.

The first selector matches those elements that have the attribute specified, and that attribute's value is the value specified in the selector. However, this selector will also match if the attribute has multiple values separated by spaces, and one of those values is the value specified in the selector.

The syntax of the selector is element[attribute~=value].

Here's an example. Assume that you have the following HTML:
<div id="content header">
    <span>CSS 2 is now a Recommendation</span>
</div>
<div id="content gist">
    <span>The standards body has approved the various new features in CSS 2.</span>
</div>
<div id="content body">
    <span>Detailed info on CSS 2 is found here.</span>
</div>
and the following CSS
div[id~=header] {text-transform:uppercase;}
div[id~=gist] {font-style: italic;}
the output will be:
The first property rule matched the first <div> tag because the id attribute has the word, header, in it. Similarly, the second property rule matched the second <div> tag because the id attribute has the word, gist, in it.
Again, as in previous selectors, the selector matches only if the attribute matches case-sensitively.

The second attribute sub-string selector matches those elements that have the attribute specified, and that attribute's value is the value specified in the selector. However, this selector will also match if the attribute has multiple values separated by hyphens, and that hyphenated set of values starts with the value specified in the selector.

The syntax of the selector is element[attribute|=value].

Let's look at an example. Assume the following HTML:
<div id="header-content">
    <span>CSS 2 is now a Recommendation</span>
</div>
<div id="gist-content">
    <span>The standards body has approved the various new features in CSS 2.</span>
</div>
<div id="body-content">
    <span>Detailed info on CSS 2 is found here.</span>
</div>
and the CSS applied is:
div[id|=header] {text-transform:uppercase;}
div[id|=gist] {font-style: italic;}
then the output we get is:
As before, the first property rule matched the first <div> tag, and the second property rule matched the second <div> tag.
Again, the attribute value is matched case-sensitively.

Child selectors

CSS 2 introduces the concept of child selectors, where you can specify which children of an element should your selector match. Note that this is different from the descendant selectors we saw in the CSS 1 section - the child selectors in CSS 2 only select children, while the descendant selector can go deeper in the tree.

CSS 2 introduces 2 child selectors, while CSS 3 introduces a lot more. Let's take a look at the 2 CSS 2 child selectors below:

:first-child pseudo-class

The :first-child pseudo-class matches those elements which are the first child of a parent element. In the pseudo-class, you only specify the type of the child element - no information about the parent needs to be provided. The pseudo-class searches for all elements of that type, and then runs through each result to check if it is a child of some other element, and if yes, checks further to determine if it is the first child. The result of this check gives a list of elements for which the style is applied.

The syntax is child-element:first-child.

Consider this example HTML:
<span>under the body tag</span><div>
    <span>First child under the div tag</span>
    <span>Second child under the div tag</span>
</div>
</body>
and this CSS that is applied on that HTML:
span:first-child {color:blue;}
the output we get is:
What happened here is that all tags inside the <body> tag are children of the <body> tag. Thus, the <span> tag immediately following the <body> tag is treated as a child, and it is the first child of the <body> tag. Thus the styling applied to it.

For the <span> tags inside the <div> tag, the first <span> tag is the first child of the <div> & hence got the styling applied, while the second <span> did not.

Again, the advantage here is that we got this styling applied without the need for any special elements to wrap around the first child. Also, in cases of DHTML, the browser will take care of applying the style in case children are added/removed.

Generic child selector

Another child selector available is the more generic version of the :first-child pseudo-class. It is used to match any element that is a child of any other element. This selector allows you to specify even the parent element of the targeted child element.

The syntax is parent > child.

Assume the following HTML:
<div>
    <span>first span</span>
    <span>second span</span>
    <ul>
        <li>
            <span>This is the first list item.</span>
        </li>
        <li> This is the second list item.</li>
    </ul>
</div>
and the following CSS:
div>span {color:blue;}
the output we get is:
The first two <span> tags under the <div> tag are matched. This is because the selector matches all children, not just the first child.

The <span> tags inside the <ul> tags are not matched because the selector only matches children of the parent, not grandchildren & other descendants.

Adjacent sibling selectors

CSS 2 also introduces the concept of sibling element selectors. The sibling selector is used to select those elements which have a specified sibling element that appears before them. Note that the specified sibling must appear before & not after.

The syntax for this selector is sibling + targeted_element. The style will be applied to targeted_element.

Consider this HTML:
<div>
    <ul>
        <li>
            <span>This is the first list item.</span>
        </li>
    </ul>
</div>
<span>First span under div</span>
<span>Second span under div</span>
and this CSS:
div+span {color:blue;}
then the output is:
What's happening here is that the first <span> tag has the style applied because its siblings is the <div> tags specified in the selector, and that <div> tag appears before the <span> tag.

Also, the <span> tags inside the <ul> tag are not selected by the selector since they do not have any siblings. Similar, the final <span> tag is also not selected since it's immediate preceding sibling is another <span> tag.

New pseudo-elements

CSS 2 introduces 2 new pseudo-elements, ::before & ::after. These pseudo-elements are used to render content either before or after the element. What's the big deal, you may ask? The deal is this: Sometimes you have a need to render repeating content, and the repeating content may need to appear either before or after a set of elements. An example of such a usecase would be adding red asterisks after every label in a form to indicate required fields. Instead of writing it into the HTML, you can style it as a CSS rule.

Assume we have the following form:
<form>
    <label for=id_no class=required>Identification Number</label>
    <input type=text name=id_no />
    <br>
    <label for=name>Name</label>
    <input type=text name=name />
    <br>
    <input type=submit />
</form>
with this CSS:
.required::after
{
    content: "*";
    color: red;
}
It results in this output:

Examples of other repeating content are page numbers, chapter numbers, etc.
Note that in CSS 3, the single colon (:) at the beginning of each pseudo-element has been replaced by the double colon (::). Thus, if you're trying out these examples on a modern browser, remember to use double colons.For convenience, my example code already has them replaced.

New pseudo-classes

The :lang pseudo-class

CSS 2 introduces a new pseudo-element, :lang. This pseudo-element is used to set styles based on the language of the document. The language may be set by multiple mechanisms depending upon the markup language, but in HTML, it is usually set using the lang attribute on the <html> tag.

Consider the HTML:
<html lang="fr">
    <head>
        <title>E:lang</title>
        <link href="lang.css" rel=stylesheet />
    </head>
    <body>
        <div>some content</div>
    </body>
</html>
and the content in lang.css:
div:lang(fr) {color:blue;}
then the output is that the text is rendered in blue colour.

If the language was changed to English("en") in the HTML, but not in the CSS file, then the text will be rendered in the browser's default colour.

The :hover & :focus pseudo-classes

The :hover pseudo-class is used to apply styling when the mouse pointer is currently over the element. The :focus pseudo-class is used to apply styling when the element receives focus, which can be due to mouse-click or a keyboard event.

As an example, consider the following HTML:
<input type=submit />
with the following CSS applied:
input:hover {cursor: pointer;}
input:focus {color: blue; border: 2px black solid;}
The following output is obtained when we press the Tab key to move focus to the button:

 You will also notice that if you move the mouse over the button, the mouse pointer will become the hand pointer instead of the typical arrow pointer.

No comments: