-
Notifications
You must be signed in to change notification settings - Fork 3.2k
Add name attribute for grouping details elements into an exclusive accordion #9400
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 5 commits
c88bbf7
8639043
cba81a3
51471c9
40c8996
5134538
8d04f03
9b76787
b7042fa
b945c9c
20490af
0c64396
65a26ed
e3ede80
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -59454,6 +59454,7 @@ dictionary <dfn dictionary>FormDataEventInit</dfn> : <span>EventInit</span> { | |
| <dd>One <code>summary</code> element followed by <span>flow content</span>.</dd> | ||
| <dt><span data-x="concept-element-attributes">Content attributes</span>:</dt> | ||
| <dd><span>Global attributes</span></dd> | ||
| <dd><code data-x="attr-details-name">name</code></dd> | ||
| <dd><code data-x="attr-details-open">open</code></dd> | ||
| <dt><span | ||
| data-x="concept-element-accessibility-considerations">Accessibility considerations</span>:</dt> | ||
|
|
@@ -59465,6 +59466,7 @@ dictionary <dfn dictionary>FormDataEventInit</dfn> : <span>EventInit</span> { | |
| interface <dfn interface>HTMLDetailsElement</dfn> : <span>HTMLElement</span> { | ||
| [<span>HTMLConstructor</span>] constructor(); | ||
|
|
||
| [<span>CEReactions</span>] attribute DOMString <span data-x="dom-details-name">name</span>; | ||
| [<span>CEReactions</span>] attribute boolean <span data-x="dom-details-open">open</span>; | ||
| };</code></pre> | ||
| </dd> | ||
|
|
@@ -59485,6 +59487,11 @@ interface <dfn interface>HTMLDetailsElement</dfn> : <span>HTMLElement</span> { | |
| <p>The rest of the element's contents <span>represents</span> the additional information or | ||
| controls.</p> | ||
|
|
||
| <p>The <dfn element-attr for="details"><code data-x="attr-details-name">name</code></dfn> content | ||
| attribute gives the name of the group of related <code>details</code> elements that the element is | ||
| a member of. Opening one member of this group causes other members of the group to close. If the | ||
|
dbaron marked this conversation as resolved.
Outdated
|
||
| attribute is specified, its value must not be the empty string.</p> | ||
|
|
||
| <p>The <dfn element-attr for="details"><code data-x="attr-details-open">open</code></dfn> content | ||
| attribute is a <span>boolean attribute</span>. If present, it indicates that both the summary and | ||
| the additional information is to be shown to the user. If the attribute is absent, only the | ||
|
|
@@ -59511,30 +59518,85 @@ interface <dfn interface>HTMLDetailsElement</dfn> : <span>HTMLElement</span> { | |
| exists, user agents can still provide this ability through some other user interface | ||
| affordance.</p> | ||
|
|
||
| <p>Whenever the <code data-x="attr-details-open">open</code> attribute is added to or removed from | ||
| a <code>details</code> element, the user agent must <span>queue an element task</span> on the | ||
| <span>DOM manipulation task source</span> given then <code>details</code> element that runs the | ||
| following steps, which are known as the <dfn>details notification task steps</dfn>, for this | ||
| <code>details</code> element:</p> | ||
| <p>The <dfn>details name group</dfn> that contains a <code>details</code> element <var>a</var> | ||
| also contains all the other <code>details</code> elements <var>b</var> that fulfill all of the | ||
| following conditions:</p> | ||
|
|
||
| <ul> | ||
|
|
||
|
dbaron marked this conversation as resolved.
Outdated
|
||
| <li>Both <var>a</var> and <var>b</var> are in the same <span>tree</span>, and the | ||
| <span>root</span> of that tree is a <code>Document</code> or a <code>ShadowRoot</code>.</li> | ||
|
|
||
| <li>They both have a <code data-x="attr-details-name">name</code> attribute, their <code | ||
| data-x="attr-details-name">name</code> attributes are not empty, and the value of <var>a</var>'s | ||
|
dbaron marked this conversation as resolved.
Outdated
|
||
| <code data-x="attr-details-name">name</code> attribute equals the value of <var>b</var>'s <code | ||
| data-x="attr-details-name">name</code> attribute.</li> | ||
|
|
||
| </ul> | ||
|
|
||
| <p>The following <span data-x="concept-element-attributes-change-ext">attribute change | ||
| steps</span>, given <var>element</var>, <var>localName</var>, <var>oldValue</var>, | ||
| <var>value</var>, and <var>namespace</var>, are used for all <code>details</code> elements:</p> | ||
|
|
||
| <ol> | ||
| <li><p>If <var>namespace</var> is not null, then return.</p></li> | ||
|
dbaron marked this conversation as resolved.
Outdated
|
||
|
|
||
| <li><p>If <var>localName</var> is not <code data-x="attr-details-open">open</code>, then | ||
| return.</p></li> | ||
|
dbaron marked this conversation as resolved.
Outdated
|
||
|
|
||
| <li> | ||
| <p>If another <span data-x="concept-task">task</span> has been <span data-x="queue an element | ||
| task">queued</span> to run the <span>details notification task steps</span> for this | ||
| <code>details</code> element, then return.</p> | ||
| <p>If one of <var>oldValue</var> or <var>value</var> is null and the other is not null, then | ||
| <span>queue an element task</span> on the <span>DOM manipulation task source</span> given the | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should it matter whether ShadowRoot's host is connected or not? In other words, can there be details name groups which aren't connected to a document?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't really have an opinion on this. I'd be happy to add such a requirement, although I don't currently have such a requirement in the spec and I don't think I have such a requirement in the implementation.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What is the purpose of this requirement? Compared to e.g. radio buttons which just require being in the same tree.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think disconnected radio groups work so great in practice, they generally complicate the implementation quite a bit (because any node can suddenly be the "owner" of the radio group). Unless there's a strong use case for this to work, maybe keeping it to DocumentOrShadowRoot is nicer? See bug 1685926 for a bug related to them not working properly in Gecko that I was aware of. Pretty sure other similar bugs exist :)
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It would be nice if we were consistent, but mostly from a theoretical purity perspective. You could imagine web developers wanting to get the exclusivity behavior while they prepare a tree pre-insertion, but I'm not sure if that's realistic. If there are implementer concerns, I'm happy to keep it to DocumentOrShadowRoot. This is an area that would benefit from extensive WPTs though. To list the cases that I can think of:
|
||
| <code>details</code> element that runs the following steps, which are known as the <dfn>details | ||
| notification task steps</dfn>, for <var>element</var>:</p> | ||
|
|
||
| <p class="note">When the <code data-x="attr-details-open">open</code> attribute is toggled | ||
| several times in succession, these steps essentially get coalesced so that only one event is | ||
| fired.</p> | ||
| <ol> | ||
| <li> | ||
| <p>If another <span data-x="concept-task">task</span> has been <span data-x="queue an | ||
| element task">queued</span> to run the <span>details notification task steps</span> for | ||
| <var>element</var>, then return.</p> | ||
|
|
||
| <p class="note">When the <code data-x="attr-details-open">open</code> attribute is toggled | ||
| several times in succession, these steps essentially get coalesced so that only one event is | ||
| fired.</p> | ||
|
dbaron marked this conversation as resolved.
Outdated
|
||
| </li> | ||
|
|
||
| <li><p><span data-x="concept-event-fire">Fire an event</span> named <code | ||
| data-x="event-toggle">toggle</code> at <var>element</var>.</p></li> | ||
| </ol> | ||
| </li> | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I want to call out this "in tree order" as a substantive open issue. For a start, it doesn't match the behavior that I've described in the explainer, implemented in Chromium, and tested for in the WPT test. That behavior is insertion order rather than tree order. However, as discussed in #9390, it seems that specifying insertion order is a substantial amount of work (and would require patching both DOM and HTML so that the removal steps can be invoked with the necessary information to connect all removed elements to their old root prior to the removal). However, there was a reason for choosing insertion order. In particular, my motivation was based on the following points:
However, there doesn't appear to be an existing pattern of specifications defining use of insertion order for sets of elements. It's common for sets of observers/listeners or similar. So I'm curious what folks think of the tradeoff here. I'm aware of four options:
The current PR specifies tree order, whereas the explainer, implementation, and tests do insertion order. So something definitely needs to change here, but I didn't want to do all the spec work for the second option above without getting some feedback first. I'm curious what folks think of the choices here... or whether you see flaws in the above analysis.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Another alternative for the insertion order edits would be using the hook proposed in whatwg/dom#1185. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The case when details is connect to document needs to be defined. Dealing only with open attribute setting isn't enough to handle document parsing. The first open details element might not be the first details in the group.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you're asking about defining enforcement of exclusivity during document parsing -- then it was an intentional design decision not to enforce the exclusivity during document parsing, on the grounds that it would introduce too much complexity and probably breaking of invariants. See this section of the explainer. If that's not what you're asking about -- could you explain further? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How is that any different to radio groups? Parsing use case should be supported.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's different from radio groups in that the open/closed state is stored in an attribute, and I believe changing attributes during parsing or during dom insertion is problematic (at least partly because of mutation events, although I think there may be other reasons). (Also, I really still am interested in feedback on the "in tree order" issue that started this thread.)
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd say that we should aim for either tree order or formally-specified insertion order. I'm confident #9390 is solvable, and indeed, it seems like it would be good to solve the bugs you uncovered there regardless. From a theoretical purity perspective, I generally prefer tree order. As you say, it's what the rest of the spec ecosystem does. I'm curious to get a sense how bad, exactly, the inefficiency of using tree order is. How many nodes would you need to sort at the time of interaction? Do you have a rough idea of how slow such sorting would be, e.g. on a low-end mobile device?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think the actual performance cost of doing tree order at time of user interaction probably isn't that bad -- it's a function of the number of details elements involved and their depth in the dom tree, and the former seems unlikely to be particluarly large. I suspect it's O(count * log(count) * depth) but I haven't checked this carefully. I'll have to figure out of there's a reasonable existing way to reuse existing Chromium code to do this...
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In https://chromium-review.googlesource.com/c/chromium/src/+/4617028 I've changed the Chromium implementation and the tests to do this in tree order. This does match other aspects of the platform. The way Chromium tends to do things that require "in tree order" is simply to traverse the entire tree, or in some cases traverse some known-relevant subtree. In some cases there's caching of the result of that traversal, with varying cleverness. In this case I didn't bother with that; it's now just a full traversal of the tree of the document or shadow root. I think that's likely to have acceptable speed -- and it did substantially reduce the amount of code involved. (I removed most of the code that I wrote for this feature in the first place!) So I think this is probably settled now, but I wanted to leave this thread open for a bit in case others had comments on that.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fwiw I also think tree order was the way to go here |
||
|
|
||
| <li><p><span data-x="concept-event-fire">Fire an event</span> named <code | ||
| data-x="event-toggle">toggle</code> at the <code>details</code> element.</p></li> | ||
| <li> | ||
| <p>If <var>oldValue</var> is null, <var>value</var> is not null, and <var>element</var> has a | ||
|
dbaron marked this conversation as resolved.
Outdated
|
||
| <code data-x="attr-details-name">name</code> attribute that is not empty, then:</p> | ||
|
|
||
| <ol> | ||
| <li> | ||
| <p>Let <var>groupMembers</var> be a list of elements, containing all elements in | ||
|
dbaron marked this conversation as resolved.
Outdated
dbaron marked this conversation as resolved.
Outdated
|
||
| <var>element</var>'s <span>details name group</span> except for <var>element</var>, in | ||
|
dbaron marked this conversation as resolved.
Outdated
|
||
| <span>tree order</span>.</p> | ||
|
|
||
| <p class="note">This is done because mutation events could be run during this algorithm, and | ||
| the mutation event listeners could change the members of the <span>details name group</span> | ||
| or the <span>tree order</span>. | ||
| </li> | ||
|
|
||
| <li> | ||
| <p><span data-x="list iterate">For each</span> element <var>otherElement</var> of | ||
| <var>groupMembers</var>:</p> | ||
| <ol> | ||
| <li><p>If the <code data-x="attr-details-open">open</code> attribute is set on | ||
|
dbaron marked this conversation as resolved.
|
||
| <var>otherElement</var>, <span data-x="concept-element-attributes-remove">remove</span> the | ||
|
dbaron marked this conversation as resolved.
Outdated
|
||
| <code data-x="attr-details-open">open</code> attribute on <var>otherElement</var>. | ||
| </ol> | ||
| </li> | ||
| </ol> | ||
| </li> | ||
| </ol> | ||
|
|
||
| <p>The <dfn attribute for="HTMLDetailsElement"><code data-x="dom-details-open">open</code></dfn> | ||
| IDL attribute must <span>reflect</span> the <code data-x="attr-details-open">open</code> content | ||
| attribute.</p> | ||
| <p>The <dfn attribute for="HTMLDetailsElement"><code data-x="dom-details-name">name</code></dfn> | ||
| and <dfn attribute for="HTMLDetailsElement"><code data-x="dom-details-open">open</code></dfn> | ||
| IDL attributes must <span>reflect</span> the respective content attributes of the same name.</p> | ||
|
|
||
| </div> | ||
|
|
||
|
|
@@ -59611,6 +59673,32 @@ interface <dfn interface>HTMLDetailsElement</dfn> : <span>HTMLElement</span> { | |
|
|
||
| </div> | ||
|
|
||
| <div class="example"> | ||
|
|
||
|
dbaron marked this conversation as resolved.
Outdated
|
||
| <p>The following example shows the <code data-x="attr-details-name">name</code> attribute of the | ||
| <code>details</code> element being used to create an exclusive accordion, a set of | ||
| <code>details</code> elements where a user action to open one <code>details</code> element causes | ||
| any open <code>details</code> to close.</p> | ||
|
|
||
| <pre><code class="html"><section class="characteristics"> | ||
| <details name="frame-characteristics"> | ||
| <summary>Material</summary> | ||
| The picture frame is made of solid oak wood. | ||
| </details> | ||
| <details name="frame-characteristics"> | ||
| <summary>Size</summary> | ||
| The picture frame fits a photo 40cm tall and 30cm wide. | ||
| The frame is 45cm tall, 35cm wide, and 2cm thick. | ||
| </details> | ||
| <details name="frame-characteristics"> | ||
| <summary>Color</summary> | ||
| The picture frame is available in its natural wood | ||
| color, or with black stain. | ||
| </details> | ||
| </section></code></pre> | ||
|
|
||
| </div> | ||
|
|
||
| <div class="example"> | ||
|
|
||
| <p>Because the <code data-x="attr-details-open">open</code> attribute is added and removed | ||
|
|
@@ -129304,6 +129392,7 @@ interface <dfn interface>External</dfn> { | |
| <td><code>summary</code>*; | ||
| <span data-x="Flow content">flow</span></td> | ||
| <td><span data-x="global attributes">globals</span>; | ||
| <code data-x="attr-details-name">name</code>; | ||
| <code data-x="attr-details-open">open</code></td> | ||
| <td><code>HTMLDetailsElement</code></td> | ||
| </tr> | ||
|
|
@@ -131575,6 +131664,11 @@ interface <dfn interface>External</dfn> { | |
| <span data-x="attr-fe-name">form-associated custom elements</span> | ||
| <td> Name of the element to use for <span>form submission</span> and in the <code data-x="dom-form-elements">form.elements</code> API <!--or: Name of the element to use in the <code data-x="dom-form-elements">form.elements</code> API. --> | ||
| <td> <a href="#attribute-text">Text</a>* | ||
| <tr> | ||
| <th> <code data-x="">name</code> | ||
| <td> <code data-x="attr-details-name">details</code> | ||
| <td> Name of group of mutually-exclusive <code>details</code> elements | ||
| <td> <a href="#attribute-text">Text</a>* | ||
| <tr> | ||
| <th> <code data-x="">name</code> | ||
| <td> <code data-x="attr-form-name">form</code> | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.