One of the more interesting things I've found lately in HTML5 are features that replicate things developers have used JavaScript for in the past. For example, I love that you can do basic form validation with just HTML tags and attributes. Another one I've found recently is the <details> tag.
Both MDN and the official spec have this to say about the tag:
The details element represents a disclosure widget from which the user can obtain additional information or controls.
"Disclosure widget" - wtf is that? Turns out it makes a lot more sense if you actually see it. Here is an example of the widget:
See the little arrow? Clicking it opens content beneath it:
Simple enough, and as I said, I'm sure you've seen this done in JavaScript many times before. So how is it done with the details tag?
<details>
<summary>This is the Title</summary>
<p>
This is my content.
</p>
</details>
Pretty simple, right? The complete content is wrapped within the details tag. The browser used the content in the summary tag as the title. That's it. In case you're curious, you can default the block to being open by adding an open attribute: <details open>.
(Edit in Feb 2018 - I had to update some links here and you should note the text around this image is not out of date - support is pretty good!) So how well is this supported? Currently not terribly well:
On mobile, support is pretty good. On desktop, IE and Firefox are the big ones missing. But here's the great thing. When it "breaks", you still have the exact same content. Here is a screen shot from Firefox of the past example.
Works perfectly well even when it doesn't work - which to me is a great thing. Here is a slightly more full example. I think a FAQ is a great way to make use of this tag. Instead of a huge page of content, you can use the summary tag for questions and the end user can choose which questions they want to display. I built the following by making use of my incredible design skills and some content from the PhoneGap FAQ:
<style>
summary {
font-weight: bold;
font-size: 2em;
font-family: 'adobe-clean','HelveticaNeue',Helvetica,Arial,sans-serif;
}
p {
font-family: 'adobe-clean','HelveticaNeue',Helvetica,Arial,sans-serif;
}
details {
margin-bottom: 10px;
}
</style>
<details>
<summary>Q: What is PhoneGap?</summary>
<p>
A: PhoneGap is an open source solution for building cross-platform mobile apps with standards-based Web technologies like HTML, JavaScript, CSS.
</p>
</details>
<details>
<summary>Q: How much does PhoneGap cost?</summary>
<p>
A: PhoneGap is an open source implementation of open standards and FREE. That means developers and companies can use PhoneGap for mobile applications that are free, commercial, open source, or any combination of these.
</p>
</details>
<details>
<summary>Q: What is the difference between PhoneGap and Cordova?</summary>
<p>
In October 2011, PhoneGap was donated to the Apache Software Foundation (ASF) under the name Apache Cordova. Through the ASF, future PhoneGap development will ensure open stewardship of the project. It will remain free and open source under the Apache License, Version 2.0.
</p>
<p>
PhoneGap is an open source distribution of Cordova. Think about Cordova’s relationship to PhoneGap like WebKit’s relationship to Safari or Chrome.
</p>
</details>
You can run this code here, and as I said, no matter what browser you use you will be able to read the content.
In the previous example I did a bit of styling to the textual content, but what about the "control", i.e. the arrow? StackOverflow came to the rescue with this suggestion for styling the arrow (or in this case, hiding it):
details summary::-webkit-details-marker {
display:none;
}
I also didn't like the "focus" border around the summary so you can remove it too:
details summary:focus {
display: none;
}
Finally - you may notice the there is no cursor change when you mouseover the arrow. You can fix that too:
details summary {
cursor:pointer;
background-color: #0c9837;
}
If you want to see an example of this, check out my demo here: https://static.raymondcamden.com/demos/2013/sep/17/test2.html Forgive me for the color choices - I should have used Kuler.
So, what if you do want to use some JavaScript with the tags? I built a simple example that simply reports if the detail block is open or closed. Here is the complete template:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title></title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width">
</head>
<body>
<details id="testDetails">
<summary>This is the Title</summary>
<p>
This is my content.
</p>
</details>
<script>
document.addEventListener("DOMContentLoaded", init, false);
var testD;
function init() {
testD = document.querySelector("#testDetails");
var summary = document.querySelector("#testDetails summary");
summary.addEventListener("click", function(e) {
var open = !testD.hasAttribute("open");
console.log(open);
}, false);
}
</script>
</body>
</html>
I assume most of this makes sense. I have pointers towards both the details block as a whole as well as the summary. On the actual click event, I see if the open attribute is present. I negate the value as - from what I can tell - the event fired before the detail block either opened or closed. You can run this yourself in my demo here: https://static.raymondcamden.com/demos/2013/sep/17/test3.html
Finally, I thought it would be kinda cool to add in support for delayed loading of the detail content. This demo is not very robust (it assumes a data-url attribute always) but it gives you an idea of how it could be done.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title></title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width">
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js"></script>
</head>
<body>
<details id="testDetails" data-details="foo.html">
<summary>This is the Title</summary>
</details>
<script>
var testD;
var loaded = false;
$(document).ready(function() {
testD = $("#testDetails");
$("#testDetails summary").on("click", function(e) {
var that = this;
var open = !testD.attr("open");
if(open && !loaded) {
/*
Techincally we may NOT be loaded yet, but I only want the first one to start the request
*/
loaded = true;
var url = testD.data("details");
$.get(url).then(function(res) {
$(that).after(res);
console.dir(that);
});
}
});
});
</script>
</body>
</html>
You can view this demo here: https://static.raymondcamden.com/demos/2013/sep/17/test4.html