The Evolution of Selectors
-
Upload
dave-arthur -
Category
Technology
-
view
148 -
download
2
Transcript of The Evolution of Selectors
THE EVOLUTION OF SELECTORS - CSS Brigade - May 29, 2014Dave Arthur
OVERVIEW OF TALK1. Brief intro to how CSS evolves
2. Highlight some level 2/3 selectors
3. Look at some level 3/4 selectors
4. Talk about selector performance and maintenance
CSS IS CONSTANTLY EVOLVINGSelectors are now at Level 4. What does that mean?
Since CSS3, the spec is broken up into “modules”, each defining aspecific part of CSS. [Click image below to see live W3C page]
SELECTOR LEVELS
LEVEL 1ID, class, type/tag, descendantcombinator, :link, :visited,:active, :first-letter,:first-line
LEVEL 2Level 1 + universal(*), attribute,more combinators, :hover,:focus, :before, :after,:first-child
LEVEL 3Level 1 + 2 + structural pseudo-classes incl. :last-child and:nth-child(), UI pseudo-classes,negation (:not()) and :targetpseudo-classes
LEVEL 4Level 1 + 2 + 3 + more UI pseudo-classes, :matches(), :has(), timedimensional pseudo-classes (e.g.text to speech), link pseudo-classes,drag-and-drop-related pseudo-classes, grid-related pseudo-classes
LEVEL 2 SELECTOR HIGHLIGHTS
CHILD SELECTOR (COMBINATOR)X > Y
Instead of targeting all descendants of a particular container, itwill only target the direct children.
E.g.: Styling a nested news list
Transport Minister Lisa Raitt said electronicdevices can be used during takeoff, ascent,descent, landing.
Charlie Angus, Francoise Boivin warngovernment to tread carefully on internetprivacy.
Ottawa to allow air passengers to useelectronic devices on takeoff, landing
Flight attendants resist Transport Canada’s move to cut their numbers
Airline travel: 5 things you need to know about your flight rights
NDP wants privacy, security experts to probewarrantless data gathering
Declaration on mass surveillance calls for new privacy measures
Cyberbullying bill surveillance powers alarm Ontario privacy watchdog
<section class="news"> <ul class="news-list"> <li> <article class="article"> <h4 class="article-title"><a href="#"></a></h4> <figure class="article-thumb"><a href="#"><img></a></figure> <p class="article-desc"></p> <ul> <li><a href="#"></a></li> <li><a href="#"></a></li> </ul> </article> </li> <li> <article class="article"> <h4 class="article-title"><a href="#"></a></h4> <figure class="article-thumb"><a href="#"><img></a></figure> <p class="article-desc"></p> <ul> <li><a href="#"></a></li> <li><a href="#"></a></li> </ul> </article> </li> </ul></section>
Check out the for full HTMLCodePen
Using a descendant selector - over-rides needed:
.news-list li { /* Descendant */ list-style: none; padding: 0.5em; margin-bottom: 0.5em; border-bottom: 1px solid #d5d5d5;}
.article li { list-style: square; font-size: 0.9em; padding: 0; /* over-ride */ border-bottom: none; /* over-ride */}
Using a child selector - cleaner:
.news-list > li { /* Child selector - only selects top-level lis */ list-style: none; padding: 0.5em; border-bottom: 1px solid #d5d5d5;}
.news-list li { /* Descendant - common property in all lis (DRY) */ margin-bottom: 0.5em;}
.article li { list-style: square; font-size: 0.9em; /* no over-ride of bottom border or padding needed */}
Check out the for full CSSCodePen
ADJACENT SIBLING SELECTOR (COMBINATOR)
X + Y
Targets elements (Y) which have a particular element (X) direclypreceeding it.
E.g.: Simple inline menu with visual separators
• • •Home About Work Contact
<nav class="menu" role="navigation"> <ul class="menu-list"> <li><a href="#">Home</a></li> <li><a href="#">About</a></li> <li><a href="#">Work</a></li> <li><a href="#">Contact</a></li> </ul></nav>
.menu-list > li:before { content: '\2022'; padding-right: 0.5em; margin-left: 0.5em; }.menu-list > li:first-child:before { content: none;}
Cleaner way using “+” combinator
<nav class="menu" role="navigation"> <ul class="menu-list"> <li><a href="#">Home</a></li> <li><a href="#">About</a></li> <li><a href="#">Work</a></li> <li><a href="#">Contact</a></li> </ul></nav>
.menu-list > li + li:before { content: '\2022'; padding-right: 0.5em; margin-left: 0.5em; }
Check out the for exampleCodePen
ATTRIBUTE SELECTORS (LEVEL 2 & 3)elem[attr="value"] 2elem[attr̂="str"] 3elem[attr$="str"] 3elem[attr*="str"] 3
Full list of attribute selectors:http://dev.w3.org/csswg/selectors4/#attribute-selectors
elem[attr="value"]Since there are many different input element types (especiallywith HTML5) this selector is great for targeting specific types.
input[type="text"], input[type="email"] { border: 1px solid #999; padding: 5px 10px; }
input[type="submit"] { background-color: #0065BD; color: #fff; border-radius: 10px; padding: 10px; }
elem[attr̂="str"]Selects elements which have an attribute value beginning with a
particular substring.
E.g. Styling links with different URLs:
Contact us by: Secure Form Email
<section class="contact"> <h4>Contact us by:</h4> <a href="https://secure.domain.com/contact">Secure Form</a> <a href="mailto:[email protected]">Email</a></section>
.contact > a:before { font-family: 'icomoon'; /* other font declarations */ margin-right: 0.25em;}
.contact > a[href̂="https://secure.domain.com"]:before { content: "\e602";}
.contact > a[href̂="mailto"]:before { content: "\e601";}
elem[attr$="str"]Selects elements which have an attribute value ending with a
particular substring.
E.g. Styling links to different file types:
Download file: PDF Word
<section class="file-download"> <h4>Download file:</h4> <a href="http://domain.com/downloads/file.pdf">PDF</a> <a href="http://domain.com/downloads/file.doc">Word</a></section>
.file-download > a:before { font-family: 'icomoon'; /* other font declarations */ margin-right: 0.25em;}
.file-download > a[href$=".pdf"]:before { content: "\e603";}
.file-download > a[href$=".doc"]:before { content: "\e604";}
elem[attr*="str"]Selects elements which the substring somewhere in the attribute
value.
Great for modular code. E.g. Styling icon links with different typesof icons:
Follow us: Twitter YouTube
<section class="social"> <h4>Follow us:</h4> <a class="social-link" href="#"><i class="icon-font-twitter"></i>Twitter</a> <a class="social-link" href="#"><i class="icon-sprite-youtube"></i>YouTube</a></section>
i[class*="icon-"] { display: inline-block;}i[class*="icon-font-"]:before { font-family: 'icomoon'; /* other font declarations */}.icon-font-twitter:before { content: "\e606";}i[class*="icon-sprite-"] { background: url(lib/images/icon-sprite.png) no-repeat; /* other sprite declarations */}.icon-sprite-youtube { width: 24px; height: 28px; background-position: 0 0;}.social-link:hover > .icon-sprite-youtube { background-position: 0 -28px;}
LEVEL 3 & 4 SELECTOR HIGHLIGHTSStructural nth-child() / nth-of-type() 3
Logical :not(), :matches() 3/4
Relational :has() 4
:nth-child()Now in Level 3 we can choose any child of a containing element.
Heard ’round Springfield
Full demo and code
HTML:
<section class="character-list"> <article class="char simpsons"> <a href="#"> <img src="homer-simpson.jpg"><q>...</q> </a> </article> <article class="char flandereses"> <a href="#"> <img src="maude-flanders.jpg"><q>...</q> </a> </article> <article class="char bouviers"> <a href="#"> <img src="selma-bouvier.jpg"><q>...</q> </a> </article> ...</section>
Default positioning of quote bubble:
.char { position: relative;}.char q { position: absolute; top: -70%; left: -50%; /* other styles */}.char q:before, .char q:after { position: absolute; /* other styles */}.char q:before { right: 50%; bottom: -30px; /* other styles */}.char q:after { right: 52%; bottom: -50px; /* other styles */}
nth-child() positioning of quote bubble:
/* Left-most column */.char:nth-child(6n+1) q { left: -100%;}.char:nth-child(6n+1) q:after { right: 45%;}/* 2nd column in from left */.char:nth-child(6n+2) q { left: -85%;}.char:nth-child(6n+2) q:after { right: 40%;}/* Right-most column */.char:nth-child(6n+6) q { left: 20%;}.char:nth-child(6n+6) q:before { right: 50%;}.char:nth-child(6n+6) q:after { right: 60%;}
:nth-child()We get problems if we mix element types.
SIMPSONS
FLANDERESES
HTML:
<section class="character-list"> <h4 class="section-title">Simpsons</h4> <article class="char simpsons"> <a href="#"> <img src="homer-simpson.jpg"><q>...</q> </a> </article> ... <h4 class="section-title">Flandereses</h4> <article class="char flandereses"> <a href="#"> <img src="ned-flanders.jpg"><q>...</q> </a> </article> ...</section>
Fixed with :nth-of-type()SIMPSONS
FLANDERESES
Full demo and code
nth-of-type() positioning of quote bubble:
/* Left-most column */.char:nth-of-type(6n+1) q { left: -100%;}.char:nth-of-type(6n+1) q:after { right: 45%;}/* 2nd column in from left */.char:nth-of-type(6n+2) q { left: -85%;}.char:nth-of-type(6n+2) q:after { right: 40%;}/* Right-most column */.char:nth-of-type(6n+6) q { left: 20%;}.char:nth-of-type(6n+6) q:before { right: 50%;}.char:nth-of-type(6n+6) q:after { right: 60%;}
:not(s) & :not(s1[, s2]*)As of Level 3 we can exclude an element(s) from selections. When Level 4 is supported :not() will take a selector list.
<section class="character-list"> <article class="char simpsons"> <a href="#"> <img src="homer-simpson.jpg"><q>...</q> </a> </article> <article class="char flandereses"> <a href="#"> <img src="maude-flanders.jpg"><q>...</q> </a> </article> <article class="char bouviers"> <a href="#"> <img src="selma-bouvier.jpg"><q>...</q> </a> </article></section>
.char:not(.simpsons) img { opacity: 0.3;}.char:not(.simpsons) q { display: none;}
:matches(s1[, s2]*)When supported :matches() will allow us to include a selectoror group of selectors in the selection.
Previous slide uses the vendor prefixed selector to simulate what:matches() will do. :matches() is currently not supported in browsers I’vetested. Note: I would not recommend using the :any() selector as it’s on its wayout.
:any()
/* Using :matches() - STANDARD but no support yet */.char:matches(.simpsons, .flandereses) img { border-color: #0065BD;}
/* Using vendor prefixed :any() - NON-STANDARD */.char:-moz-any(.simpsons, .flandereses) img { border-color: #0065BD;}.char:-webkit-any(.simpsons, .flandereses) img { border-color: #0065BD;}
/* Using classes */.simpsons img, .flanders img { border-color: #0065BD;}
One useful application of :matches() would be for styling HTML5 headings.Since the document outline has been revised you can have multiple h1s on a page.Example CSS from MDN doing it the looong way:
/* Level 0 */h1 { font-size: 30px;}/* Level 1 */section h1, article h1, aside h1, nav h1 { font-size: 25px;}/* Level 2 */section section h1, section article h1, section aside h1, section nav h1,article section h1, article article h1, article aside h1, article nav h1,aside section h1, aside article h1, aside aside h1, aside nav h1,nav section h1, nav article h1, nav aside h1, nav nav h1, { font-size: 20px;}/* Level 3 *//* ... don't even think about it*/
When :matches() is supported:
/* Level 0 */h1 { font-size: 30px;}/* Level 1 */:matches(section, article, aside, nav) h1 { font-size: 25px;}/* Level 2 */:matches(section, article, aside, nav):matches(section, article, aside, nav) h1 { font-size: 20px;}/* Level 3 */:matches(section, article, aside, nav):matches(section, article, aside, nav):matches(section, article, aside, nav) h1 { font-size: 15px;}
:has(rs1[, rs2]*)Apparently new this year. When supported the :has() relational pseudo willallow us to select for elements which have a particular relationship to theelement(s) passed as parameters.
Examples from W3C spec:
Matches only a elements that contain an img child:
a:has(> img)
Matches a dt element immediately followed by another dt element:
dt:has(+ dt)
Matches section elements that don’t contain any heading elements:
section:not(:has(h1, h2, h3, h4, h5, h6))
NEED TO SUPPORT OLDER IE VERSIONS? is JS polyfill for Level 3 selectors.Selectivzr
SELECTOR PERFORMANCE
SOME CONSIDERATIONSBrowsers read selectors from right to left
Ideally want right-most “key” selector to be specific
IDs and classes are most efficient
Combinators (descendant, child, etc), attributes, pseudo-classes are not as efficient
I know what you are thinking...
OTHER CONSIDERATIONS. will be dead soon. Browsers are much
better than they used to be!
You should probably focus on other web performance bestpractices first (e.g. minimizing, using fonts/SVG or sprites,optimizing image file sizes, CDNs, caching, etc.)
There isn’t one solution–different websites require differentstrategies
Focus on maintainability...
IE6 is dead IE7 and 8
MAKING YOUR CSS MORE MAINTAINABLEDon’t tag qualify id or class selectors
Don’t “over-qualify” selectors
Minimize selector depth
Minimize general descendant/child selectors
Modularize code
Choose a naming/coding convention (SMACSS, BEM, etc.)
Decide what level of CSS efficiency is right for your site.
DON’T TAG QUALIFY ID OR CLASS SELECTORS/* Qualified */div#main-content {}ul.menu-list {}
/* Unqualified */#main-content {}.menu-list {}
ISSUESSelectors tied to particular mark up pattern
Increasing selector specificity
DON’T “OVER-QUALIFY” SELECTORS/* Over-qualified */.nav ul li a {}
/* Better */.nav a {}
ISSUESBrowsers will need to look up document tree anyways. Addingul and li not necessary
Increasing selector specificity
MINIMIZE SELECTOR DEPTH/* Not great */.content section ul li a {}
/* Better choices - Add class "list" to ul */.list > li > a {} .list a {}
The key is we reduced the number of levels the browser has towalk up.
Alternatives? Put class on li or individual a elements. Betterefficiency but at cost of maintenance?
MINIMIZE GENERAL DESCENDANT/CHILDSELECTORS
Especially involving universal selector. E.g.:
#main-content section {}ul li {}.nav > * {}
Alternatives? Can you use classes? Better mark up?
MODULARIZE CODEKeeps selector depth low
Great for portability with minimal CSS revisions
CSS preprocessors make modularizing easy. Create differentpartial file for each module.
<article class="news-item"> <h2 class="news-item-title">Title of Article</h2> <a href="#"><img class="news-item-thumb" src="" alt=""></a> <p class="news-item-excerpt">Excerpt</p></article>
.news-item {} /* module base class */
.news-item-title {}
.news-item-thumb {}
.news-item-excerpt {}
THANK YOU FOR LISTENING! QUESTIONS?Additional Resources