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
Archived Comments
I use summary/details in a real world website to markup events.
https://www.southside-crew....
Those global variables have to go. Short, generic names like "loaded" can can easily become a problem if the browser environment happens to define a built-in with such a name.
Consider inline demos via jsFiddle or codepen. People could then fork those demos to improve the code, and then, if you liked the changes, you would just have to replace the demo URL in your blog post text source to insert the improved demo. #easy
Sime... I agree in general but in practical terms I want my demo to be short and simple.
As for your second comment - I was using Gists for my code blocks, but didn't like the external dependency. When github went down (not very often of course), it broke my site completely. And in all the time I used them, no one ever (afaik) forked the code to do anything with it. :)
I've re-factored the JavaScript code here: https://gist.github.com/sim...
I'd like to emphasize these two key changes:
1. Using local variables instead of global variables, in order to avoid name collisions in the global namespace
2. Performing local DOM queries instead of global ones, for performance reasons
Note that your "It was just a quick demo" argument (https://twitter.com/cfjedim... is not valid here as performing the above mentioned changes does not require extra work (which you *do* want to avoid when creating quick demos). Instead, it's about choosing the right pattern: You need variables, so you have to make a choice of either creating global or local variables. Likewise, you need to perform DOM queries, so you, again, have to make a choice of either performing global or local queries (where possible). In both cases, doing it local it the better choice. In the first case, your demo is more robust, and in the second case it performs better.
Simi, I definitely appreciate the time you took to update the code and comment. I have to say that I try my best here to share the best code possible in the most approachable manner possible. It is a hard balance sometimes. When I'm focused on particular aspect in a demo I may not always spend time thinking about the other aspects. As an example, if I'm demonstrating an AJAX call to some cool new service, I may forget to add in basic error support or detection if the user is offline. Those things are important, of course, but I'm not sure it is always practical here on the blog.
As to my comment about it being just a quick demo, well, the time it takes to write better code isn't just a case of the # of letters typed per se. I don't write my best code on first draft. I'm still learning how to write *better* JavaScript and it doesn't come naturally all the time. In this blog post, I really felt the focus was on the tags and the JS example was really just an offhand "gee whiz" type thing.
I never want to lead people astray or promote bad practices, but at the same time, the blog is a living document. So I think it is fair to say that what I do here would be (possibly) pretty different from what I'd do in more of a real application.
I can't disagree with any point you've made here, but I hope you understand my view here as the author. I really do need to think a bit more about my code though and will definitely endeavor to do, and as always, I'd definitely appreciate your comments.
I'm wondering why you are speaking of "tags" instead of "elements". Or rather, why you're treating the two expressions as similar. In my opinion, an element is the syntactical unit that's relevant here – a tag (or rather the start and end tags) is only its "physical" representation.
@Christian: I guess I didn't really consider it that important of a difference. Do you?
Great article. Thanks!
Hope to see the tags implemented in IE11 and FF ... 27?
Cheers
I think we'll see a big uptick in usage after Firefox implements it. I'm curious if we'll see a lot of "abuse." Like a login link that reveals a login form. Or would that be a valid use? It probably is legit - since the spec says "additional controls." Perhaps there is no wrong way to use it semantically.
Chris, I could imagine a login and registration form using a separate details block to make it a bit slimmer, but that may be overkill. I do not pretend to be a UX/UI guy at all, so take my opinion with a huge grain of salt.
Maybe the login form would be normal, and the registration form collapsed since it is bigger.
My ugly example: http://codepen.io/cfjedimas...
I found this link about how to handle it in js, if its not supported.
http://tyleruebele.com/webl...
Interesting.
its so easy to use Ray, I had to find a way to work around those other browsers... pretty slick simple, and self coding, I like it.
Thanks
Oh yeah, definitely possible with JS. But I love that we can skip it. :)