You are here


On being irritated

Seth Godin - Tue 25th Oct 2016 19:10
Irritation is a privilege. It's the least useful emotion, one that we never seek out. People in true distress are never irritated. Someone who is hungry or drowning or fleeing doesn't become irritated. And of course, irritation rarely helps us...        Seth Godin
Categories: thinktime

Moral hazard and inhumanity

Seth Godin - Mon 24th Oct 2016 19:10
One bit of economic reasoning says, "If there are no consequences, people will make bad choices." Don't let big banks get bailouts, because if we do, bankers will take bigger risks. So, make sure that the dentist is expensive (and...        Seth Godin
Categories: thinktime

Hardware is sexy, but it's software that matters

Seth Godin - Sun 23rd Oct 2016 19:10
You can make software if you choose to. Not just the expected version of software that runs on a computer, but the metaphorical idea of rules and algorithms designed to solve problems and connect people... Apple started as a hardware...        Seth Godin
Categories: thinktime

Beating yourself up

Seth Godin - Sat 22nd Oct 2016 19:10
This odd behavior mostly shows up when others are criticizing us, disappointed or angry about something we did. Odd because it's so useless. In those moments, there are already plenty of other people beating you up. Save yourself the trouble....        Seth Godin
Categories: thinktime

Pet peeves

Seth Godin - Fri 21st Oct 2016 20:10
Peeves make lousy pets. They're difficult to care for, they eat a lot, and they don't clean up after themselves.        Seth Godin
Categories: thinktime

Making a new decision based on new information

Seth Godin - Thu 20th Oct 2016 19:10
This is more difficult than it sounds. To some people, it means admitting you were wrong. (But of course, you weren't wrong. You made a decision based on one set of facts, but now you're aware of something new.) To...        Seth Godin
Categories: thinktime


Seth Godin - Wed 19th Oct 2016 20:10
If you're sharing a cab to the airport with a stranger, what happens if he's two inches taller than you? Probably nothing. There's nothing to distract, or to cause discomfort. You make small talk. What if he's a little shorter...        Seth Godin
Categories: thinktime

JavaScript for Web Designers: DOM Scripting

a list apart - Wed 19th Oct 2016 01:10

A note from the editors: We’re pleased to share an excerpt from Chapter 5 of Mat Marquis' new book, JavaScript for Web Designers, available now from A Book Apart.

Before we do anything with a page, you and I need to have a talk about something very important: the Document Object Model. There are two purposes to the DOM: providing JavaScript with a map of all the elements on our page, and providing us with a set of methods for accessing those elements, their attributes, and their contents.

The “object” part of Document Object Model should make a lot more sense now than it did the first time the DOM came up, though: the DOM is a representation of a web page in the form of an object, made up of properties that represent each of the document’s child elements and subproperties representing each of those elements’ child elements, and so on. It’s objects all the way down.

window: The Global Context

Everything we do with JavaScript falls within the scope of a single object: window. The window object represents, predictably enough, the entire browser window. It contains the entire DOM, as well as—and this is the tricky part—the whole of JavaScript.

When we first talked about variable scope, we touched on the concept of there being “global” and “local” scopes, meaning that a variable could be made available either to every part of our scripts or to their enclosing function alone.

The window object is that global scope. All of the functions and methods built into JavaScript are built off of the window object. We don’t have to reference window constantly, of course, or you would’ve seen a lot of it before now—since window is the global scope, JavaScript checks window for any variables we haven’t defined ourselves. In fact, the console object that you’ve hopefully come to know and love is a method of the window object:

window.console.log function log() { [native code] }

It’s hard to visualize globally vs. locally scoped variables before knowing about window, but much easier after: when we introduce a variable to the global scope, we’re making it a property of window—and since we don’t explicitly have to reference window whenever we’re accessing one of its properties or methods, we can call that variable anywhere in our scripts by just using its identifier. When we access an identifier, what we’re really doing is this:

function ourFunction() { var localVar = "I’m local."; globalVar = "I’m global."; return "I’m global too!"; }; undefined window.ourFunction(); I’m global too! window.localVar; undefined window.globalVar; I’m global.

The DOM’s entire representation of the page is a property of window: specifically, window.document. Just entering window.document in your developer console will return all of the markup on the current page in one enormous string, which isn’t particularly useful—but everything on the page can be accessed as subproperties of window.document the exact same way. Remember that we don’t need to specify window in order to access its document property—window is the only game in town, after all.

document.head <head>...</head> document.body <body>...</body>

Those two properties are themselves objects that contain properties that are objects, and so on down the chain. (“Everything is an object, kinda.”)

Using the DOM

The objects in window.document make up JavaScript’s map of the document, but it isn’t terribly useful for us—at least, not when we’re trying to access DOM nodes the way we’d access any other object. Winding our way through the document object manually would be a huge headache for us, and that means our scripts would completely fall apart as soon as any markup changed.

But window.document isn’t just a representation of the page; it also provides us with a smarter API for accessing that information. For instance, if we want to find every p element on a page, we don’t have to write out a string of property keys—we use a helper method built into document that gathers them all into an array-like list for us. Open up any site you want—so long as it likely has a paragraph element or two in it—and try this out in your console:

document.getElementsByTagName( "p" ); [<p>...</p>, <p>...</p>, <p>...</p>, <p>...</p>]

Since we’re dealing with such familiar data types, we already have some idea how to work with them:

var paragraphs = document.getElementsByTagName( "p" ); undefined paragraphs.length 4 paragraphs[ 0 ]; <p>...</p>

But DOM methods don’t give us arrays, strictly speaking. Methods like getElementsByTagName return “node lists,” which behave a lot like arrays. Each item in a nodeList refers to an individual node in the DOM—like a p or a div—and will come with a number of DOM-specific methods built in. For example, the innerHTML method will return any markup a node contains—elements, text, and so on—as a string:

var paragraphs = document.getElementsByTagName( "p" ), lastIndex = paragraphs.length – 1, /* Use the length of the `paragraphs` node list minus 1 (because of zero-indexing) to get the last paragraph on the page */ lastParagraph = paragraphs[ lastIndex ]; lastParagraph.innerHTML; And that’s how I spent my summer vacation.

Fig 5.1: First drafts are always tough.

The same way these methods give us access to information on the rendered page, they allow us to alter that information, as well. For example, the innerHTML method does this the same way we’d change the value of any other object: a single equals sign, followed by the new value.

var paragraphs = document.getElementsByTagName( "p" ), firstParagraph = paragraphs[ 0 ]; firstParagraph.innerHTML = "Listen up, chumps:"; "Listen up, chumps:"

JavaScript’s map of the DOM works both ways: document is updated whenever any markup changes, and our markup is updated whenever anything within document changes (Fig 5.1).

Likewise, the DOM API gives us a number of methods for creating, adding, and removing elements. They’re all more or less spelled out in plain English, so even though things can seem a little verbose, it isn’t too hard to break down.

DOM Scripting

Before we get started, let’s abandon our developer console for a bit. Ages ago now, we walked through setting up a bare-bones HTML template that pulls in a remote script, and we’re going to revisit that setup now. Between the knowledge you’ve gained about JavaScript so far and an introduction to the DOM, we’re done with just telling our console to parrot things back to us—it’s time to build something.

We’re going to add a “cut” to an index page full of text—a teaser paragraph followed by a link to reveal the full text. We’re not going to make the user navigate to another page, though. Instead, we’ll use JavaScript to show the full text on the same page.

Let’s start by setting up an HTML document that links out to an external stylesheet and external script file—nothing fancy. Both our stylesheet and script files are empty with .css and .js extensions, for now—I like to keep my CSS in a /css subdirectory and my JavaScript in a /js subdirectory, but do whatever makes you most comfortable.

<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <link rel="stylesheet" type="text/css" href="css/style.css"> </head> <body> <script src="js/script.js"></script> </body> </html>

We’re going to populate that page with several paragraphs of text. Any ol’ text you can find laying around will do, including—with apologies to the content strategists in the audience—a little old-fashioned lorem ipsum. We’re just mocking up a quick article page, like a blog post.

<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <link rel="stylesheet" type="text/css" href="css/style.css"> </head> <body> <h1>JavaScript for Web Designers</h1> <p>In all fairness, I should start this book with an apology—not to you, reader, though I don’t doubt that I’ll owe you at least one by the time we get to the end. I owe JavaScript a number of apologies for the things I’ve said to it during the early years of my career, some of which were strong enough to etch glass.</p> <p>This is my not-so-subtle way of saying that JavaScript can be a tricky thing to learn.</p> [ … ] <script src="js/script.js"></script> </body> </html>

Feel free to open up the stylesheet and play with the typography, but don’t get too distracted. We’ll need to write a little CSS later, but for now: we’ve got scripting to do.

We can break this script down into a few discrete tasks: we need to add a Read More link to the first paragraph, we need to hide all the p elements apart from the first one, and we need to reveal those hidden elements when the user interacts with the Read More link.

We’ll start by adding that Read More link to the end of the first paragraph. Open up your still-empty script.js file and enter the following:

var newLink = document.createElement( "a" );

First, we’re intializing the variable newLink, which uses document.createElement( "a" ) to—just like it says on the tin—create a new a element. This element doesn’t really exist anywhere yet—to get it to appear on the page we’ll need to add it manually. First, though, <a></a> without any attributes or contents isn’t very useful. Before adding it to the page, let’s populate it with whatever information it needs.

We could do this after adding the link to the DOM, of course, but there’s no sense in making multiple updates to the element on the page instead of one update that adds the final result—doing all the work on that element before dropping it into the page helps keep our code predictable.

Making a single trip to the DOM whenever possible is also better for performance—but performance micro-optimization is easy to obsess over. As you’ve seen, JavaScript frequently offers us multiple ways to do the same thing, and one of those methods may technically outperform the other. This invariably leads to “excessively clever” code—convoluted loops that require in-person explanations to make any sense at all, just for the sake of shaving off precious picoseconds of load time. I’ve done it; I still catch myself doing it; but you should try not to. So while making as few round-trips to the DOM as possible is a good habit to be in for the sake of performance, the main reason is that it keeps our code readable and predictable. By only making trips to the DOM when we really need to, we avoid repeating ourselves and we make our interaction points with the DOM more obvious for future maintainers of our scripts.

So. Back to our empty, attribute-less <a></a> floating in the JavaScript ether, totally independent of our document.

Now we can use two other DOM interfaces to make that link more useful: setAttribute to give it attributes, and innerHTML to populate it with text. These have a slightly different syntax. We can just assign a string using innerHTML, the way we’d assign a value to any other object. setAttribute, on the other hand, expects two arguments: the attribute and the value we want for that attribute, in that order. Since we don’t actually plan to have this link go anywhere, we’ll just set a hash as the href—a link to the page you’re already on.

var newLink = document.createElement( "a" ); newLink.setAttribute( "href", "#" ); newLink.innerHTML = "Read more";

You’ll notice we’re using these interfaces on our stored reference to the element instead of on document itself. All the DOM’s nodes have access to methods like the ones we’re using here—we only use document.getElementsByTagName( "p" ) because we want to get all the paragraph elements in the document. If we only wanted to get all the paragraph elements inside a certain div, we could do the same thing with a reference to that div—something like ourSpecificDiv.getElementsByTagName( "p" );. And since we’ll want to set the href attribute and the inner HTML of the link we’ve created, we reference these properties using newLink.setAttribute and newLink.innerHTML.

Next: we want this link to come at the end of our first paragraph, so our script will need a way to reference that first paragraph. We already know that document.getElementsByTagName( "p" ) gives us a node list of all the paragraphs in the page. Since node lists behave like arrays, we can reference the first item in the node list one by using the index 0.

var newLink = document.createElement( "a" ); var allParagraphs = document.getElementsByTagName( "p" ); var firstParagraph = allParagraphs[ 0 ]; newLink.setAttribute( "href", "#" ); newLink.innerHTML = "Read more";

For the sake of keeping our code readable, it’s a good idea to initialize our variables up at the top of a script—even if only by initializing them as undefined (by giving them an identifier but no value)—if we plan to assign them a value later on. This way we know all the identifiers in play.

So now we have everything we need in order to append a link to the end of the first paragraph: the element that we want to append (newLink) and the element we want to append it to (firstParagraph).

One of the built-in methods on all DOM nodes is appendChild, which—as the name implies—allows us to append a child element to that DOM node. We’ll call that appendChild method on our saved reference to the first paragraph in the document, passing it newLink as an argument.

var newLink = document.createElement( "a" ); var allParagraphs = document.getElementsByTagName( "p" ); var firstParagraph = allParagraphs[ 0 ]; newLink.setAttribute( "href", "#" ); newLink.innerHTML = "Read more"; firstParagraph.appendChild( newLink );

Now—finally—we have something we can point at when we reload the page. If everything has gone according to plan, you’ll now have a Read More link at the end of the first paragraph on the page. If everything hasn’t gone according to plan—because of a misplaced semicolon or mismatched parentheses, for example—your developer console will give you a heads-up that something has gone wrong, so be sure to keep it open.

Pretty close, but a little janky-looking—our link is crashing into the paragraph above it, since that link is display: inline by default (Fig 5.2).

Fig 5.2: Well, it’s a start.

We have a couple of options for dealing with this: I won’t get into all the various syntaxes here, but the DOM also gives us access to styling information about elements—though, in its most basic form, it will only allow us to read and change styling information associated with a style attribute. Just to get a feel for how that works, let’s change the link to display: inline-block and add a few pixels of margin to the left side, so it isn’t colliding with our text. Just like setting attributes, we’ll do this before we add the link to the page:

var newLink = document.createElement( "a" ); var allParagraphs = document.getElementsByTagName( "p" ); var firstParagraph = allParagraphs[ 0 ]; newLink.setAttribute( "href", "#" ); newLink.innerHTML = "Read more"; = "inline-block"; = "10px"; firstParagraph.appendChild( newLink );

Well, adding those lines worked, but not without a couple of catches. First, let’s talk about that syntax (Fig 5.3).

Fig 5.3: Now we’re talking.

Remember that identifiers can’t contain hyphens, and since everything is an object (sort of), the DOM references styles in object format as well. Any CSS property that contains a hyphen instead gets camel-cased: margin-left becomes marginLeft, border-radius-top-left becomes borderRadiusTopLeft, and so on. Since the value we set for those properties is a string, however, hyphens are just fine. A little awkward and one more thing to remember, but this is manageable enough—certainly no reason to avoid styling in JavaScript, if the situation makes it absolutely necessary.

A better reason to avoid styling in JavaScript is to maintain a separation of behavior and presentation. JavaScript is our “behavioral” layer the way CSS is our “presentational” layer, and seldom the twain should meet. Changing styles on a page shouldn’t mean rooting through line after line of functions and variables, the same way we wouldn’t want to bury styles in our markup. The people who might end up maintaining the styles for the site may not be completely comfortable editing JavaScript—and since changing styles in JavaScript means we’re indirectly adding styles via style attributes, whatever we write in a script is going to override the contents of a stylesheet by default.

We can maintain that separation of concerns by instead using setAttribute again to give our link a class. So, let’s scratch out those two styling lines and add one setting a class in their place.

var newLink = document.createElement( "a" ); var allParagraphs = document.getElementsByTagName( "p" ); var firstParagraph = allParagraphs[ 0 ]; newLink.setAttribute( "href", "#" ); newLink.setAttribute( "class", "more-link" ); newLink.innerHTML = "Read more"; firstParagraph.appendChild( newLink );

Now we can style .more-link in our stylesheets as usual:

.more-link { display: inline-block; margin-left: 10px; }

Much better (Fig 5.4). It’s worth keeping in mind for the future that using setAttribute this way on a node in the DOM would mean overwriting any classes already on the element, but that’s not a concern where we’re putting this element together from scratch.

Fig 5.4: No visible changes, but this change keeps our styling decisions in our CSS and our behavioral decisions in JavaScript.

Now we’re ready to move on to the second item on our to-do list: hiding all the other paragraphs.

Since we’ve made changes to code we know already worked, be sure to reload the page to make sure everything is still working as expected. We don’t want to introduce a bug here and continue on writing code, or we’ll eventually get stuck digging back through all the changes we made. If everything has gone according to plan, the page should look the same when we reload it now.

Now we have a list of all the paragraphs on the page, and we need to act on each of them. We need a loop—and since we’re iterating over an array-like node list, we need a for loop. Just to make sure we have our loop in order, we’ll log each paragraph to the console before we go any further:

var newLink = document.createElement( "a" ); var allParagraphs = document.getElementsByTagName( "p" ); var firstParagraph = allParagraphs[ 0 ]; newLink.setAttribute( "href", "#" ); newLink.setAttribute( "class", "more-link" ); newLink.innerHTML = "Read more"; for( var i = 0; i < allParagraphs.length; i++ ) { console.log( allParagraphs[ i ] ); } firstParagraph.appendChild( newLink );

Your Read More link should still be kicking around in the first paragraph as usual, and your console should be rich with filler text (Fig 5.5).

Fig 5.5: Looks like our loop is doing what we expect.

Now we have to hide those paragraphs with display: none, and we have a couple of options: we could use a class the way we did before, but it wouldn’t be a terrible idea to use styles in JavaScript for this. We’re controlling all the hiding and showing from our script, and there’s no chance we’ll want that behavior to be overridden by something in a stylesheet. In this case, it makes sense to use the DOM’s built-in methods for applying styles:

var newLink = document.createElement( "a" ); var allParagraphs = document.getElementsByTagName( "p" ); var firstParagraph = allParagraphs[ 0 ]; newLink.setAttribute( "href", "#" ); newLink.setAttribute( "class", "more-link" ); newLink.innerHTML = "Read more"; for( var i = 0; i < allParagraphs.length; i++ ) { allParagraphs[ i ].style.display = "none"; } firstParagraph.appendChild( newLink );

If we reload the page now, everything is gone: our JavaScript loops through the entire list of paragraphs and hides them all. We need to make an exception for the first paragraph, and that means conditional logic—an if statement, and the i variable gives us an easy value to check against:

var newLink = document.createElement( "a" ); var allParagraphs = document.getElementsByTagName( "p" ); var firstParagraph = allParagraphs[ 0 ]; newLink.setAttribute( "href", "#" ); newLink.setAttribute( "class", "more-link" ); newLink.innerHTML = "Read more"; for( var i = 0; i < allParagraphs.length; i++ ) { if( i === 0 ) { continue; } allParagraphs[ i ].style.display = "none"; } firstParagraph.appendChild( newLink );

If this is the first time through of the loop, the continue keyword skips the rest of the current iteration and then—unlike if we’d used break—the loop continues on to the next iteration.

If you reload the page now, we’ll have a single paragraph with a Read More link at the end, but all the others will be hidden. Things are looking good so far—and if things aren’t looking quite so good for you, double-check your console to make sure nothing is amiss.

Now that you’ve got a solid grounding in the DOM, let’s really dig in and see where to take it from here.

Want to read more?

The rest of this chapter (even more than you just read!) goes even deeper—and that’s only one chapter out of Mat’s hands-on, help-you-with-your-current-project guide. Check out the rest of JavaScript for Web Designers at A Book Apart.

Categories: thinktime

You're invited to an all-day Q&A in New York in December

Seth Godin - Tue 18th Oct 2016 21:10
We've been planning this one for months... On Saturday, December 10, I'll be running an all-day session in New York. You can find all the details and tickets by visiting this site. I want to connect you to other people...        Seth Godin
Categories: thinktime

Ketchup and the third-party problem

Seth Godin - Mon 17th Oct 2016 19:10
Sir Kensington's Ketchup is better ketchup. Most adults who try it agree that it's more delicious, a better choice. Alas, Heinz has a host of significant advantages, including dominant shelf space, a Proustian relationship with our childhood and unlimited money...        Seth Godin
Categories: thinktime


Seth Godin - Sun 16th Oct 2016 19:10
The things that break all at once aren’t really a problem. You note that they’ve broken, and then you fix them. The challenge is corrosion. Things that slowly fade, that eventually become a hassle--it takes effort and judgment to decide...        Seth Godin
Categories: thinktime

Your discomfort zone

Seth Godin - Sat 15th Oct 2016 19:10
Most of us need an external stimulus to do our best work. It helps to have an alarm clock if you want to get out of bed before dawn. A presentation. A deadline. A live performance. The threat of foreclosure,...        Seth Godin
Categories: thinktime

Cutting through the clutter

Seth Godin - Fri 14th Oct 2016 19:10
You're trying to get through all the noise and the distraction and the clutter with your message. Here's the thing: You are the noise and the distraction and the clutter. Just because it's important to you doesn't mean it's important...        Seth Godin
Categories: thinktime

What would happen...

Seth Godin - Thu 13th Oct 2016 20:10
if we chose to: Get better at setting and honoring deadlines Help one more person, each day Sit in the front row Ask a hard question every time we go to a meeting Give more and take less Learn to...        Seth Godin
Categories: thinktime

Cable news

Seth Godin - Wed 12th Oct 2016 19:10
What if the fear and malaise and anger isn't merely being reported by cable news... What if it's being caused by cable news? What if ubiquitous video accompanied by frightening and freaked out talking heads is actually, finally, changing our...        Seth Godin
Categories: thinktime

Using CSS Mod Queries with Range Selectors

a list apart - Wed 12th Oct 2016 01:10

Recently, I was asked to build a simple list that would display in a grid—one that could start with a single element and grow throughout the day, yet alway be tidy regardless of the length. So, as you do sometimes when you’re busy with one thing and asked if you can do something completely different, I tried to think of any reason why it couldn’t be done, came up blank, and distractedly said, “Yes.”

At the time, I was working on a London-based news organization’s website. We’d spent the previous year migrating their CMS to the Adobe AEM platform while simultaneously implementing a responsive UI—both big improvements. Since that phase was complete, we were starting to focus on finessing the UI and building new features. The development project was divided into a number of small semiautonomous teams. My team was focusing on hub pages, and I was leading the UI effort.

Each hub page is essentially a list of lists, simply there to help readers find content that interests them. As you can imagine, a news website is almost exclusively made of content lists! A page full of generic vertical lists would be unhelpful and unappealing; we wanted readers to enjoy browsing the content related to their sphere of interest. Sections needed to be distinct and the lists had to be both individually distinguishable and sit harmoniously together. In short, the visual display was critical to the usability and effectiveness of the entire page.

That “simple list” I said I’d build would be high profile, sitting in its own panel near the top of a hub page and serving to highlight a specific point of interest. Starting with one item and growing throughout the day as related articles were published, the list needed to be a rectangular grid rather than a single column, and never have “leftover” items in the last row. And no matter how many child elements it contained at any given moment, it had to stay tidy and neat because it would display above the fold. Each item would be more or less square, with the first item set at 100% width, the second two at 50%, and all subsequent items 33% and arranged in rows of three. My simple list suddenly wasn’t so simple.

Not everyone wants a generic grid or stack of identical items—there’s something nice about selective prominence, grouped elements, and graceful line endings. These styles can be hardcoded if you know the list will always be an exact length, but it becomes more of a challenge when the length can change. How could I keep that last row tidy when there were fewer than three items?

Our intended layout would break visually as more items were added to the list.

When it came to actually building the thing, I realized that knowing the length of the list wasn’t very helpful. Having loved Heydon Pickering’s excellent article on quantity queries for CSS, I assumed I could find out the length of the list using QQs, then style it accordingly and all would be fine.

But since my list could be any length, I’d need an infinite number of QQs to meet the requirements! I couldn’t have a QQ for every eventuality. Plus, there were rumors a “Load More” button might be added down the road, letting users dynamically inject another 10 or so items. I needed a different solution.

After a minor meltdown, I asked myself, What would Lea Verou do? Well, not panicking would be a good start. Also, it would help to simplify and identify the underlying requirements. Since the list would fundamentally comprise rows of three, I needed to know the remainder from mod 3.

The “mod” query

Being able to select and style elements by the number of siblings is great, but there’s more to this than mere length. In this case, it would be much better to know if my list is divisible by a certain number rather than how long it is.

Unfortunately, there isn’t a native mod query in CSS, but we can create one by combining two selectors: :nth-child(3n) (aka the “modulo” selector) and the :first-child selector.

The following query selects everything if the list is divisible by three:

li:nth-last-child(3n):first-child, li:nth-last-child(3n):first-child ~ li { … selects everything in a list divisible by three … } Only those rows divisible by three are selected. See the Pen Using CSS Mod Queries with Range Selectors: Fig 2 by Patrick (@clanceyp) on CodePen. Cat image via Paper Bird Publishing.

Let’s talk through that code. (I use li for “list item” in the examples.)

The css selector:

li:nth-last-child(3n):first-child ~ li

Select all following siblings:

... ~ li

The first child (first li in the list, in this case):

...:first-child ...

Every third item starting from the end of the list:


That combination basically means if the first child is 3n from the end, select all of its siblings.

The query selects all siblings of the first item, but doesn’t include the first item itself, so we need to add a selector for it separately.

li:nth-last-child(3n):first-child, li:nth-last-child(3n):first-child ~ li { … styles for list items in a list divisible by 3 … }

Check out the demo and give it a try!

What about remainders?

With my mod query, I can select all the items in a list if the list is divisible by three, but I’ll need to apply different styles if there are remainders. (In the case of remainder 1, I’ll just need to count back in the CSS from the second-to-last element, instead of the last. This can be achieved by simply adding +1 to the query.)

li:nth-last-child(3n+1):first-child, li:nth-last-child(3n+1):first-child ~ li { … styles for elements in list length, mod 3 remainder = 1 … }

Ditto for remainder 2—I just add +2 to the query.

li:nth-last-child(3n+2):first-child, li:nth-last-child(3n+2):first-child ~ li { … styles for elements in list length, mod 3 remainder = 2 … } Creating a range selector

Now I have a way to determine if the list length is divisible by any given number, with or without remainders, but I still need to select a range. As with mod query, there isn’t a native CSS range selector, but we can create one by combining two selectors: :nth-child(n) (i.e., “everything above”) and :nth-child(-n) (i.e., “everything below”).

This allows us to select items 3 to 5, inclusive:

li:nth-child(n+3):nth-child(-n+5){ ... styles for items 3 to 5 inclusive ... } We’ve selected a range: cats 3, 4, and 5.

True, that could just as easily be achieved with simple :nth-child(n) syntax and targeting the item positions directly—li:nth-child(3), li:nth-child(4), li:nth-child(5){ ... }—but defining a start and end to a range is obviously much more versatile. Let’s quickly unpack the selector to see what it’s doing.

Selects all the items up to and including the fifth item:

li:nth-child(n+3):nth-child(-n+5){ … }

Selects all the items from the third item onwards:

li:nth-child(n+3):nth-child(-n+5){ … }

Combining the two—li:nth-child(n+3):nth-child(-n+5)—creates a range selector.

If we look at an example, we might have a product grid where the list items contain an image, title, and description. Let’s say the product image speaks for itself, so in the first row we promote the image and hide all the text. With the second and third row, we display the title and image as a thumbnail, while in subsequent rows we hide the image and show the title and description on a single line.

A product grid of our cats. We have standalone graphics in the top row, small graphics plus product titles in the second and third rows, and then we lose the graphics and only show text for all rows after that. See the Pen Using CSS Mod Queries with Range Selectors: Fig 4 by Patrick (@clanceyp) on CodePen.

By using the range selector, we can select the first three, the fourth through ninth, and the 10th onwards. This allows us to change the ranges at different breakpoints in the CSS so we can keep our product grid nice and responsive.

Notes on SCSS mixins

Since I was using a CSS preprocessor, I simplified my code by using preprocessor functions; these are SCSS mixins for creating range selectors and mod queries.

// range selector mixin @mixin select-range($start, $end){ &:nth-child(n+#{$start}):nth-child(-n+#{$end}){ @content; } } // mod query mixin @mixin mod-list($mod, $remainder){ &:nth-last-child(#{$mod}n+#{$remainder}):first-child, &:nth-last-child(#{$mod}n+#{$remainder}):first-child ~ li { @content; } }

Then in my code I could nest the mixins.

li { @include mod-list(3, 0){ @include select-range(3, 5){ // styles for items 3 to 5 in a list mod 3 remainder = 0 } } }

Which is, if nothing else, much easier to read!

Putting it all together

So now that I have a little arsenal of tools to help me deal with mods, ranges, and ranges within mods, I can break away from standard-implementation fixed length or fixed-layout lists. Creative use of mod queries and range selectors lets me apply styles to change the layout of elements.

Getting back to the original requirement—getting my list to behave—it became clear that if I styled the list assuming it was a multiple of three, then there would only be two other use cases to support:

  • Mod 3, remainder 1
  • Mod 3, remainder 2

If there was one remaining item, I’d make the second row take three items (instead of the default two), but if the remainder was 2, I could make the third row take two items (with the fourth and fifth items at 50%).

In the end, I didn’t need numerous queries at all, and the ones I did need were actually quite simple.

There was one special case: What if the list only contained two elements?

That was solved with a query to select the second item when it’s also the last child.

li:nth-child(2):last-child { ... styles for the last item if it’s also the second item ... }

The queries ultimately weren’t as hard as I’d expected; I just needed to combine the mod and range selectors.

li:nth-last-child(3n):first-child /* mod query */ ~ li:nth-child(n+3):nth-child(-n+5){ /* range selector */ ... styles for 3rd to 5th elements, in a list divisible by 3 ... }

Altogether, my CSS looked something like this in the end:

/* default settings for list (when its mod 3 remainder 0) list items are 33% wide except; the first item is 100% the second and third are 50% */ li { width: 33.33%; } li:first-child { width: 100%; } /* range selector for 2nd and 3rd */ li:nth-child(n+2):nth-child(-n+3){ width: 50%; } /* overrides */ /* mod query override, check for mod 3 remainder = 1 */ li:nth-last-child(3n+1):first-child ~ li:nth-child(n+2):nth-child(-n+3) { width: 33.33%; /* override default 50% width for 2nd and 3rd items */ } /* mod query override, check for mod 3 remainder = 2 */ li:nth-last-child(3n+2):first-child ~ li:nth-child(n+4):nth-child(-n+5) { width: 50%; /* override default 33% width for 4th and 5th items */ } /* special case, list contains only two items */ li:nth-child(2):last-child { margin-left: 25%; } Experience for yourself (and a note on browser support)

The mod queries and range selectors used in this article rely on the CSS3 selectors, so they will work in all modern browsers that support CSS3, including Internet Explorer 9 and above (but remember, IE will expect a valid doctype).

I created a small mod query generator that you can use to experiment with mod queries.

When I first came across QQs, I thought they were great and interesting but largely theoretical, without many practical real-world use cases. However, with mobile usage outstripping desktop, and responsive design now the norm, the need to display lists, target parts of lists depending on the length/mod, and display lists differently at different breakpoints has become much more common. This really brings the practical application of QQs into focus, and I’m finding more than ever that they are an essential part of the UI developer’s toolkit.

Additional resources
Categories: thinktime

Now is never (but here comes tomorrow)

Seth Godin - Tue 11th Oct 2016 20:10
Everything you're working on is an investment in tomorrow. While we can choose to enjoy the process, the end result is always at the end of an arc, always the result of many steps, of earning trust, of building a...        Seth Godin
Categories: thinktime


Seth Godin - Mon 10th Oct 2016 20:10
When creating a layout, designers put low-resolution, imperfect, non-final images, all marked "for position only." They exist to help the client understand the gestalt of the piece and to give feedback. They're temporary, parts of a whole ready to be...        Seth Godin
Categories: thinktime

Visualize the leaks

Seth Godin - Sun 09th Oct 2016 20:10
It's almost impossible to walk past a spewing faucet without stopping and trying to turn it off. We can't bear to see the waste. But our organizations leak all the time. The talented people who don't stick with the job...        Seth Godin
Categories: thinktime

"Here, I made this"

Seth Godin - Sat 08th Oct 2016 20:10
"I" as in me, you, us, the person who's on the line. This is the work of a human. The audience can make a direct connection between you and the thing you're offering. "Made" because it took effort, originality and...        Seth Godin
Categories: thinktime


Subscribe to kattekrab aggregator - thinktime