-
Notifications
You must be signed in to change notification settings - Fork 4.7k
iAPI: Preserves boolean attributes during navigation. #74217
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
Conversation
|
Warning: Type of PR label mismatch To merge this PR, it requires exactly 1 label indicating the type of PR. Other labels are optional and not being checked here.
Read more about Type labels in Gutenberg. Don't worry if you don't have the required permissions to add labels; the PR reviewer should be able to help with the task. |
|
The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the Unlinked AccountsThe following contributors have not linked their GitHub and WordPress.org accounts: @claycarpenter. Contributors, please read how to link your accounts to ensure your work is properly credited in WordPress releases. If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message. To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook. |
| const booleanAttributes: readonly string[] = [ | ||
| 'open', | ||
| 'checked', | ||
| 'disabled', | ||
| 'hidden', | ||
| 'selected', | ||
| 'readonly', | ||
| 'required', | ||
| 'autoplay', | ||
| 'controls', | ||
| 'loop', | ||
| 'muted', | ||
| ]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm still getting introduced to the iAPI code, which is not being straightforward, but something is not right with me with that list of “boolean attributes.”
The first thing that comes to my mind is to simply check for each element if they are of boolean type, instead of having to list the dozens of potential boolean types that could be useful for any use case.
Anyway, I would like to hear from @luisherranz
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi @SirLouen 👋 I'm taking a look at this PR and the related issue.
It's true that we could check if a given attribute is of boolean type, although that comparison is not straightforward. Some attributes don't directly translate to their corresponding property (e.g., readonly → readOnly, novalidate → noValidate, etc.) So, we would have to rely on a "list" for this, either all boolean attributes or the corresponding prop if they have a different name.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Regarding the list of all boolean attributes, this is what I get from Claude Opus 4.5:
Full Reference
| Attribute | Element(s) | JavaScript Property | MDN Link |
|---|---|---|---|
allowfullscreen |
<iframe> |
allowFullscreen |
MDN |
async |
<script> |
async |
MDN |
autofocus |
Form elements, <dialog> |
autofocus |
MDN |
autoplay |
<audio>, <video> |
autoplay |
MDN |
checked |
<input> |
checked |
MDN |
controls |
<audio>, <video> |
controls |
MDN |
default |
<track> |
default |
MDN |
defer |
<script> |
defer |
MDN |
disabled |
Form elements, <link> |
disabled |
MDN |
formnovalidate |
<button>, <input> |
formNoValidate |
MDN |
hidden |
Global | hidden |
MDN |
inert |
Global | inert |
MDN |
ismap |
<img> |
isMap |
MDN |
itemscope |
Global | itemScope |
MDN |
loop |
<audio>, <video> |
loop |
MDN |
multiple |
<input>, <select> |
multiple |
MDN |
muted |
<audio>, <video> |
muted |
MDN |
nomodule |
<script> |
noModule |
MDN |
novalidate |
<form> |
noValidate |
MDN |
open |
<details>, <dialog> |
open |
MDN |
playsinline |
<video> |
playsInline |
MDN |
readonly |
<input>, <textarea> |
readOnly |
MDN |
required |
<input>, <select>, <textarea> |
required |
MDN |
reversed |
<ol> |
reversed |
MDN |
selected |
<option> |
selected |
MDN |
shadowrootclonable |
<template> |
shadowRootClonable |
MDN |
shadowrootdelegatesfocus |
<template> |
shadowRootDelegatesFocus |
MDN |
shadowrootserializable |
<template> |
shadowRootSerializable |
MDN |
Attributes with Different HTML/DOM Names
| HTML Attribute | DOM Property | MDN Link |
|---|---|---|
allowfullscreen |
allowFullscreen |
MDN |
formnovalidate |
formNoValidate |
MDN |
ismap |
isMap |
MDN |
itemscope |
itemScope |
MDN |
nomodule |
noModule |
MDN |
novalidate |
noValidate |
MDN |
playsinline |
playsInline |
MDN |
readonly |
readOnly |
MDN |
shadowrootclonable |
shadowRootClonable |
MDN |
shadowrootdelegatesfocus |
shadowRootDelegatesFocus |
MDN |
shadowrootserializable |
shadowRootSerializable |
MDN |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is unmaintainable. The list may grow over time. Do you really think there isn't a more generalizable way to target them? For example, camelcase testing or something like that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, the list is quite long. 🤔 I agree that camelcase testing would be more approachable. Still, we would need a list of attributes that have their equivalent DOM prop in camelcase, right? This would have the same maintainability problem, given that the list could also grow over time.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, its almost impossible to achieve this
I've asked GPT for some ideas, and after reviewing and several iterations I got to this conclusion
Long story short:
- Gets all the elements from DOM
- Check against each element if the attribute is boolean by creating the element and adding the attribute and testing it
- There are some elements like
asyncthat arefalseby default and others likeautofocusaretrueso it test in the two conditions - Finally, it returns the result.
Conclusion: This is crazy low-performing and not viable. There could be some caching in between to slightly improve the overall performance.
Definitely the most performant way is still, to maintain a list. So at this point I wonder if there could be another implementation without having to necessarily check if it's boolean (or maybe just stick to the boolean elements we have in the inspector controls).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Preact checks the name in the element, and if it doesn't exist (because there's a camel case mismatch), it falls back to setAttribute.
`open` in `details` // true
// Preact uses elem[attr] = value;
`readonly` in `input` // false because it's readOnly, not readonly
// Preact uses elem.setAttribute(attr, value);elem.setAttribute(attr, value) works fine when the element is boolean and the value is an empty string (""), so we don't have to worry about that case.
We can simply do this to match what Preact is doing underneath:
if (attributeValue === "" && typeof elementNode[attributeName] === "boolean") {
props[attributeName] = elementNode[attributeName];
} else {
props[attributeName] = attributeValue;
}Alternative PR:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice! Thanks, @luisherranz, that's a much better approach. I should have paid more attention to the internal nuances of Preact's diffing algorithm. 😅
@vipul0425, @SirLouen, I think we can close this PR in favor of #74446.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had a hunch that there had to be a better solution. I'm not into preact so I would have never figured out. Time to close this 👍
|
Closed in favor of #74446. |
What?
Closes #74148
Why?
Boolean attributes were being ignored during navigation via the iAPI router.
How?
This PR adds checks for boolean attributes and ensures they are preserved in the VDOM.
Testing Instructions
Screenshots or screencast
Screen.Recording.2025-12-25.at.2.54.10.PM.mov