Tag Soup: Using Custom Elements to Cover Elections and Beyond

What We Learned Testing Custom Elements in a News App

From the Seattle Times election results page, the project on which we honed our approach to custom elements

An election results board is in many ways the purest test for a news app: it combines a high-traffic, high-pressure breaking news page with the challenge of scraping data feeds from a range of sources (some well-organized, many much less so). Nobody in the Seattle Times newsroom was happier, or more relieved, when our results page performed almost flawlessly on election night. A brand-new Node-based application built on our news app template, it pulled results from the Washington Secretary of State and King County, parsed and merged them, then uploaded the results as static pages to Amazon S3.

I was relieved because there was always a worry that something would go wrong with the feeds or the build process, but also because our election page served as a testbed for custom elements in a news app. Part of an upcoming platform feature called Web Components, custom elements let developers define their own HTML tags, with complex behavior and lifecycle. I’d used them before as a part of an embedded web UI framework , but not on a general-public site. Based on our experience, I’m extremely excited about their potential. To see why, let’s take a quick tour of the Web Component concept, then look at how we leveraged them for the Seattle Times election results page, before seeing how they make it easy to create reusable and sharable newsroom tools.

A Brief Primer on Web Components

Although the idea no doubt germinated from many seeds, one of the founding arguments for Web Components can be found in this blog post by Chrome engineer Alex Russell. There he complains that there’s too much “magic” in browsers: everything is either high-level elements or low-level hackery (WebGL), largely ignoring the power of JavaScript in the middle. Russell called for browsers to provide APIs that would let coders implement and explain new behavior at all layers using only HTML, CSS, and JavaScript, without any magic.

The Web Components spec attempts to achieve this by providing four new “building blocks:”

  • document.registerElement(), which creates new tags and extends existing tags with new features,
  • Shadow DOM, which hides the internal structure of those elements from external styles and scripts,
  • <template> for stamping out fresh copies of HTML in each component, and
  • HTML Imports, which provide a better mechanism for loading all the required resources.

Using the Web Component platform APIs, it’s possible to bundle up a complicated unit of functionality into a single declarative element, just like any other HTML tag. In many cases, browser developers have already been using these same tools, and they’re just exposing them to users: the date input in Chrome and the color input in Firefox are already built entirely with HTML, CSS, and JavaScript behind the scenes.

Google has a purpose-built component library called Polymer for this process, but it’s only available for IE10 and up, and our minimum-supported browser is IE9. So for creating custom elements at the Seattle Times, I set up an alternative scaffolding that only directly shims the document.registerElement() method using Andrea Giammarchi’s library. For the other parts of the spec, we provide functional alternatives: ICanHaz.JS or Dot for templating, LESS for isolating element styles, and a Grunt build process for bundling all the dependencies up into a single package.

Our Savage Elements

As a committed front-end developer, I often shrug off complaints from other developers about the vagaries of HTML and CSS, but when it comes to SVG, I suspect I know how they feel. My experiences with it have been capricious and inconsistent from browser to browser (I’m probably doing something wrong). Still, for election maps SVG images are probably still the best option, so I decided to wrap them in a custom element that would contain and control their oddities, along with a small library (named Savage) for papering over the cracks in the SVG API.

If you view the source for our results page, you’ll notice the maps embedded for statewide races and ballot measures are contained in an unfamiliar element: <svg-map>. Loading an SVG into the page is as easy as setting the src attribute on these elements, and the component takes care of requesting, caching, and injecting the SVG document. A JavaScript promise attached to the element is resolved once the image is ready, and convenience methods like eachPath() attach to this promise to perform asynchronous rendering, such as when results are added to the state map.

In the source, you’ll also notice that the innards of some of our <svg-map> elements are Handlebars templates, but these don’t appear directly in the output—what’s up with that? When an element is created for the first time, the component parses its innerHTML out as a template for the hover text on the map, then replaces that with the actual image contents. A JavaScript onhover method feeds data back to the template in response to the hover event.

Custom elements quickly proved their worth on this project. They simplified our JavaScript by bundling code up into a discrete, logical unit with a straightforward external interface. They made it easy to add maps and reposition them on the page, since they were written like any other tag. And they provided a declarative API for non-programmers like our web producers who wanted to adjust the maps or their hover text: it’s just HTML!

Introducing <responsive-frame>

I’ve enjoyed using custom elements in this project as a developer, but I think the real value comes from the way that they package up a discrete piece of UI in a way that can be used equally well by coders and non-coders in a newsroom. As a proof of concept, we’ve developed <responsive-frame>, an alternative to the Pym.js library created by NPR. Pym is a great library, and we rely on it often (we’ve even published our own branch of it for bugfixes), but even as minimal as it is, its configuration burden is relatively high for non-coders: the parent page alone requires a placeholder div, a script import, and a configuration script that calls the Pym constructor with an ID and a URL. I think it can be made easier with web components. Using our custom element, embedding a child page is as simple as including the registration script and then writing:

<responsive-frame src="child.html"></responsive-frame>

On the other side, the child page can declare any element as the “container” for embedded content by using the <responsive-child> element, or even just adding an is attribute to extend an existing element. For example, we can target the <body> of the child page this way:

<body is="responsive-child">

That’s all the configuration needed! When the elements start up on each side of the embed, they negotiate a unique ID for each iframe, and then send messages across the boundary using a JSON-based protocol when either window is resized.

If you’d like to experiment with our responsive frame, you can take a look at the source code here, or install it from Bower as component-responsive-frame. We’ve also used these new elements in our newest investigative piece, Sell Block.

A Call for Wider Adoption

From Sell Block, a new investigative feature using our custom elements

One of my favorite parts of working in newsrooms as a developer is the number of really cool, innovative browser-based tools that have come out of journalism. In the past, these libraries have been published the same way as most other front-end projects: either as jQuery plugins, or as bundles of JavaScript that a coder needs to glue together. But with custom elements, you can provide a declarative method for putting the element on the page, while still exposing its API for customization. It’s helpful to imagine packaging up something like Pro Publica’s Landline as a <landline-map>, for example, or a <boundary-input> that’s powered by Wherewolf. We’re also experimenting with a Leaflet map element that lets users quickly assemble a map with layers, markers, and GeoJSON without touching a line of JavaScript.

If you’re working on a newsroom tool that you think you might re-use, or open-source for others, I’d like to advocate for going the Web Component route. It’s not just something that we’re using at The Seattle Times: Google is pushing hard for Polymer-based components at this year’s Chrome Dev Summit, and Mozilla is using them with its Brick library in Firefox OS.

If you’re interested in getting started, feel free to build on our component template, which provides a framework for building elements and packaging them up for external use. We’d love to hear about projects that you build with it, or any bugs you might find!




Current page