The :lang
selector is very different from all other selectors (AFAIK).
Other selectors only (directly) effect the actual matched elements themselves, whereas the :lang
pseudo class is very different in that all elements within an element targeted by :lang selector are also directly targeted.
So let's say I place a border around an ul
element - only the ul
itself gets the border - not all of the list items (demo). Not so with :lang if I target that same ul which has an attribute of lang="en" - all of the list items will also get the border - to the extent that in order to override that rule (on the child !!) I have to use a selector with greater specficity (DEMO !!!).
:lang(en) {
border: 5px solid red;
}
<ul lang="en">
<li>item
<li>item
<li>item
<li>item
<li>item
</ul>
So I would like to know why the :lang selector was implemented in this very strange way. (apart from the fact that that's what it says in the spec) for the following reasons:
1) Counter-intuitive:
If we use the logic that all the descendants of the element with a lang attribute should be matched - because they share the same language - then conceptually the [lang] attribute selector should match in exactly the same way!
2) Not necessary:
a) Typically when dealing with different languages in a document you need to adjust text-related properties like font-family, font-size, color, quotes etc to suit the other language. The thing is that these properties anyway inherit such that by matching just the parent element element - all subsequent descendants get these changes. (demo)
b) If this functionality was necessary for some reason it could be achieved with the universal selector like so .parent,.parent * {}
(demo)
So I would like to know why the :lang selector was implemented in this very strange way.
It works that way because that's how we want it to work, most of the time. This way we can specify a language for the entire document, or for parts of the document, by putting the lang
attribute just once at the top level.
For example, let's say I give a quoting rule for German (see this question):
q:before { content: open-quote; }
q:after { content: close-quote; }
:lang(de) { quotes: "«" "»"; }
To make this work, I certainly do not want to have to apply the lang
attribute to every single element throughout my HTML to which I might want to apply quotes. Instead, I need only apply it to the <html>
element.
A semi-related example (see this question). Here we want the text-transform
property to work properly with Greek. For that to happen requires the language to be known. Again, we can make this work properly by specifying the language for our entire page by putting lang='el'
on the html
element.
So let's say I place a border around an ul
element - only the ul
itself gets the border - not all of the list items (demo). Not so with :lang
. If I target that same ul
which has an attribute of lang="en"
- all of the list items will also get the border (DEMO !!!).
Right, which is why you would not do things that way. If you want to target the ul
, target it some other way, or if you must, you could use an attribute selector [lang='en']
.
On the other hand, perhaps you wanted to make all Japanese text on a page red. If you've deployed the lang
attribute correctly, that is a case where you would want to target using the :lang
selector:
:lang(ja) { color: red; }
You're targetting every element with a lang
set to en
, and the lang
value does inherit. But you want to only select the ul
. So, simply add the ul
selector, as you're supposed to, and would do with every other normal pseudo-selector.
ul:lang(en) {
border: 5px solid red;
}
<ul lang="en">
<li>item</li>
<li>item</li>
<li>item</li>
<li>item</li>
<li>item</li>
</ul>
If you, for some reason, don't want to use the ul
selector, use the attribute lang
itself, since it does not inherit, like this:
[lang="en"] {
border: 5px solid red;
}
<ul lang="en">
<li>item</li>
<li>item</li>
<li>item</li>
<li>item</li>
<li>item</li>
</ul>
There's nothing counter-intuitive with the :lang
pseudo-class. All pseudo classes work this way in that they represent element states - so all elements in that particular state are targeted.
The [lang] attribute selector on the other hand will only select the elements themselves which have the attributes - because attribute selectors check for attributes not state.
Whether the :lang
attribute is absolutely necessary or if its functionality could be achieved using other selectors might be debatable, but there's certainly nothing strange with the way :lang
works.