HTML5 Rocks

HTML5 Rocks

dialog element: shipped in Chrome 37 Beta

By Eiji Kitamura at

Chrome Beta has landed its native support for <dialog> element without needing "Enable experimental Web Platform features." flag turned on.

<dialog>
  <p>This is da dialog!</p>
  <button id="close">Close</button>
</dialog>
<button id="show">Open Dialog!</button>
<script>
  var dialog = document.querySelector('dialog');
  document.querySelector('#show').onclick = function() {
    dialog.show();
  };
  document.querySelector('#close').onclick = function() {
    dialog.close();
  };
</script>

Check out more sample codes and how it works in detail with a live demo.

There are a few changes applied to the implementation since the last announcement but notable one is:

  • Non-modal <dialog> is no longer vertically centered in the viewport. It has no special positioning behavior beyond its default CSS styling.

If you are curious about further details on the spec update, check out diffs here and here.

dialog element: Modals made easy

By Eiji Kitamura at

Chrome Canary has landed support for the dialog element behind a flag. The dialog element can be used for popups in a web page.

  • show(): Open dialog.
  • close(): Close dialog. Takes an optional argument which if present dialog.returnValue is set to.
  • showModal(): Open modal dialog.
  • ::backdrop: Pseudo-element to style background behind a modal dialog.
  • close event: Fired when a dialog is closed.

Update on Dec 16th 2013

The dialog element now supports:

  • cancel event: Fired when the Escape key is pressed on a modal dialog. This event can be canceled using event.preventDefault().
  • autofocus attribute: The first form control in a modal dialog that has the autofocus attribute, if any, will be focused when the dialog is shown. If there is no such element, the first focusable element is focused.
  • form[method="dialog"]: Only valid inside a dialog. Upon form submission, it closes the dialog and sets dialog.returnValue to the value of the submit button that was used.

Check out details with a live demo and polyfill.

Turn it on by enabling "Enable experimental Web Platform features" in about://flags.

What's the CSS :scope pseudo-class for?

By Eric Bidelman at

:scope is defined in CSS Selectors 4 as:

A pseudo-class which represents any element that is in the contextual reference element set. This is is a (potentially empty) explicitly-specified set of elements, such as that specified by the querySelector(), or the parent element of a <style scoped> element, which is used to "scope" a selector so that it only matches within a subtree.

An example of using this guy is within a <style scoped> (more info):

<style>
  li {
    color: blue;
  }
</style>

<ul>
  <style scoped>
    li {
      color: red;
    } 
    :scope {
      border: 1px solid red;
    }
  </style>
  <li>abc</li>
  <li>def</li>
  <li>efg</li>
</ul>

<ul>
  <li>hij</li>
  <li>klm</li>
  <li>nop</li>
</ul>

Note: <style scoped> can be enabled in Chrome using the "Enable experimental WebKit features" flag in about:flags.

This colors the li elements in the first ul red and, because of the :scope rule, puts a border around the ul. That's because in the context of this <style scoped>, the ul matches :scope. It's the local context. If we were to add a :scope rule in the outer <style> it would match the entire document. Essentially, equivalent to :root.

Contextual elements

You're probably aware of the Element version of querySelector() and querySelectorAll(). Instead of querying the entire document, you can restrict the result set to a contextual element:

<ul>
 <li id="scope"><a>abc</a></li>
 <li>def</li>
 <li><a>efg</a></li>
</ul>
<script>
  document.querySelectorAll('ul a').length; // 2

  var scope = document.querySelector('#scope');
  scope.querySelectorAll('a').length; // 1
</script>

When these are called, the browser returns a NodeList that's filtered to only include the set of nodes that a.) match the selector and b.) which are also descendants of the context element. So in the the second example, the browser finds all a elements, then filters out the ones not in the scope element. This works, but it can lead to some bizarre behavior if you're not careful. Read on.

When querySelector goes wrong

There's a really important point in the Selectors spec that people often overlook. Even when querySelector[All]() is invoked on an element, selectors still evaluate in the context of the entire document. This means unanticipated things can happen:

scope.querySelectorAll('ul a').length); // 1
scope.querySelectorAll('body ul a').length); // 1

WTF! In the first example, ul is my element, yet I'm still able to use it and matches nodes. In the second, body isn't even a descendant of my element, but "body ul a" still matches. Both of these are confusing and not what you'd expect.

It's worth making the comparison to jQuery here, which takes the right approach and does what you'd expect:

$(scope).find('ul a').length // 0
$(scope).find('body ul a').length // 0

...enter :scope to solve these semantic shenanigans.

Fixing querySelector with :scope

WebKit recently landed support for using the :scope pseudo-class in querySelector[All](). You can test it in Chrome Canary 27.

You can use it restrict selectors to a context element. Let's see an example. In the following, :scope is used to "scope" the selector to the scope element's subtree. That's right, I said scope three times!

scope.querySelectorAll(':scope ul a').length); // 0
scope.querySelectorAll(':scope body ul a').length); // 0
scope.querySelectorAll(':scope a').length); // 1

Using :scope makes the semantics of the querySelector() methods a little more predictable and inline with what others like jQuery are already doing.

Performance win?

Not yet :(

I was curious if using :scope in qS/qSA gives a performance boost. So...like a good engineer I threw together a test. My rationale: less surface area for the browser to do selector matching means speedier lookups.

In my experiment, WebKit currently takes ~1.5-2x longer than not using :scope. Drats! When crbug.com/222028 gets fixed, using it should theoretically give you a slight performance boost over not using it.

Visualizing Shadow DOM Concepts

By Eric Bidelman at

Shadow DOM is a difficult topic to wrap your head around. It's just complex. It introduces unfamiliar concepts that we're not used to on the web. Shadow boundaries, styling scoping, event retargeting, insertion points, shadow insertion points, host nodes, distributed nodes,...the lingo goes on and on.

TRY THE DEMO

One thing that's conceptually taxing about Shadow DOM is the way your final product (DOM) is rendered by the browser. Nodes from the host node are magically swizzled into a ShadowRoot's insertion points, yet logically, still remain in the host node. Weird! So at render time, they appear as part of the shadow tree and not the original host. How this rendering takes place is one of the most confusing pieces of Shadow DOM.

A few days ago, I released a tool I've been working on called Shadow DOM Visualizer to help lessen the learning curve.

It allows you to visually see how Shadow DOM renders in the browser, something DevTools lacks today. Both black code blocks on the left are editable. Try changing the <content> insertion points, removing, or adding new ones to see how the composited (rendered) tree is affected on the right.

Mouse over the nodes in the graph to highlight the relevant markup on the left. Yay for d3.js! Blue nodes are coming from the host node. Yellow nodes come from the Shadow DOM. <content> insertion points are the bridge between the two worlds. Because they're logically in the Shadow DOM, they're colored yellow. Their blue border indicates that they invite blue host nodes into the rendering party.

Shadow DOM is available in Chrome 25 and the <template> element is available in Chrome 26 (although you only need the first to try the demo).

datalist landed in Chrome Canary

By Eiji Kitamura at

Filling out forms sometimes feel like cumbersome thing. Giving users multiple choice yet enabling them to type freely is important. The datalist element (which just landed on Chrome Canary (M20) makes this a breeze.

By using datalist, your app can define a list of suggested results users should select from. They can either select an option from the list or enter freeform text.

Live demo:
http://demo.agektmr.com/datalist/

Options can be paired with a datalist by specifying its id in an input element’s list attribute:

<input type="text" value="" list="fruits" />
<datalist id="fruits">
  <option value="Apple"></option> 
  <option value="Orange"></option> 
  <option value="Peach"></option> 
</datalist>

datalist is widely available on latest Firefox, Opera and Internet Explorer after version 10. So you don’t have to worry about compatibility too much, but if you want to make sure it works across browsers, try the following:

<datalist id="fruits">
  Pick your favorite fruit
  <select name="fruit_sel">
  <option value="Apple">Apple</option> 
  <option value="Orange">Orange</option> 
  <option value="Peach">Peach</option> 
  </select>
  or type one.
</datalist>
<input type="text" name="fruit" value="" list="fruits" />

If datalist is available on your browser, everything under the datalist except the option elements will be hidden. If you use this fallback mechanism, make sure your server catches both “fruit_sel” and “fruit” as query parameters.