Closed josepharhar closed 3 years ago
The only concern I have with this is that content-visibility: hidden
puts in things like containment which on the surface doesn't seem like it's needed for hidden-matchable
type of behavior.
Also, the proposal says that visibility: hidden
has performance problems such as not skipping layout work, but again that doesn't seem like it is a requirement for having hidden searchable content.
To be clear, I think this should absolutely work with content-visibility: hidden
, but I'd just like to point out that making it also work with visibility: hidden
might be nice.
Accidentally didn't log the discussion, so here it is for posterity:
fantasai> jarhar: Hi I'm Joey. I'm working on content-visibility: hiddne-matchable fantasai> jarhar: in parallel with HTML feature called ?? fantasai> jarhar: A lot of websites have sections, like wikipedia heycam> s/??/beforematch/ fantasai> jarhar: and find-in-page doesn't work because it's 'display: none' fantasai> jarhar: When searching for these things, want these things to be findable fantasai> jarhar: so you would send 'content-visiblity: hidden-matchable' which is same as 'content-visibility: hidden' * fantasai q+ * Zakim sees myles, fantasai on the speaker queue fantasai> jarhar: that'll find the element and fire ?? fantasai> jarhar: and page has ability to change the style to reveal the content cbiesinger> s/??/the beforematch event/ fantasai> jarhar: and after one RequestAnimationFrame fantasai> jarhar: browser will scroll to it florian> s/??/before match/ fantasai> jarhar: and that's pretty much thie ideal emilio> q+ * Zakim sees myles, fantasai, emilio on the speaker queue fremy> this is a wonderful proposalq- * Zakim sees fantasai, emilio on the speaker queue fantasai> s/thie ideal/the idea/ astearns> ack fantasai * Zakim sees emilio on the speaker queue florian> fantasai: I'm curious, in a lot of cases, it seems it should just work florian> fantasai: in the case of a details element, it should just pop open florian> fantasai: I'm a little confused as to why we wouldn't want this to happen as well fantasai> jarhar: Agree supporting content in details element fantasai> jarhar: but separate from CSS property fantasai> jarhar: for DETAILS could just say browser can change the state of DETAILS automatically fantasai> jarhar: but other case don't use DETAILS element, those use display: none fantasai> jarhar: so providing a different way smfr> q+ * Zakim sees emilio, smfr on the speaker queue fantasai> jarhar: also CSS state is maintained by page, page has opportunity to change itself jarhar: 2nd question? TabAtkins> q+ * Zakim sees emilio, smfr, TabAtkins on the speaker queue bkardell_> q+ * Zakim sees emilio, smfr, TabAtkins, bkardell_ on the speaker queue fantasai> fantasai: why wouldn't this be the default behavior for 'content-visbility: hidden' fantasai> jarhar: could maybe, but some concern around privacy mitigations fantasai> jarhar: if we fired on hidden content fantasai> jarhar: page, after it gets an event, the page is required to reveal the content or else we lock the page out of using beforematch fantasai> jarhar: we want to prevent the page from trying to figure out what the user is searching for fantasai> jarhar: and hidden-beforematch is what we're using to determine state bkardell_> actually I will just say most of the time that is probably not actually what you want and if you need me to clarify why I can readd to the queue fantasai> jarhar: a lot of pages using 'content-visilibity: hidden' already, and would create lockout, so not great bkardell_> q-- * Zakim sees emilio, smfr, TabAtkins, bkardell_ on the speaker queue fantasai> jarhar: so that's why astearns> ack emilio * Zakim sees smfr, TabAtkins, bkardell_ on the speaker queue bkardell_> q- * Zakim sees smfr, TabAtkins on the speaker queue fantasai> emilio: Find-in-page already changes the selection of the document, and that's observable now fantasai> emilio: so how useful is this mitigation fantasai> jarhar: There are other ways to observe find-in-page TabAtkins> So the answer to "why not give 'hidden' this behavior, and then add another value that has the current unmatchable behavior" is "there's already some legacy content that we'd prefer not to break if not necessary" fantasai> jarhar: e.g. by listening to scroll events * dholbert has quit (Ping timeout: 180 seconds) fantasai> jarhar: in Firefox as you type it fires selection events fantasai> jarhar: makes it easy to detec fantasai> jarhar: in Chromium not the same fantasai> jarhar: the selection is only fired when user dismisses find-in-page dialog * dauwhe has quit (Client closed connection) vmpstr> q+ * Zakim sees smfr, TabAtkins, vmpstr on the speaker queue fantasai> jarhar: so from Firefox point of view, makes sense not to have extra mitigation, but from our side it's needed * dauwhe (~dauwhe@ef89345d.public.cloak) has joined #css fantasai> emilio: Other question is, this makes find-in-page effectively asynchronous, which is not something that happens fantasai> emilio: how does window.find() work and similar things? fantasai> emilio: and why does this have to be a CSS property at all? fantasai> emilio: I think if you find text in a 'display: none' subtree, and if page reacts to it fantasai> emilio: find again or something fantasai> emilio: idk fantasai> jarhar: for async part, it's true, the whole flow is async fantasai> jarhar: first we find the match, then wait for next animation frame, then ?, then wait for next frame, then see if it was revealed fantasai> jarhar: that was seen to be necessary ...j fantasai> jarhar: based on page, which wasn't handling the style change synchronously fantasai> jarhar: but for normal find-in-page use case, whene not 'hidden-matchable' fantasai> jarhar: still keeping it synchronous fantasai> jarhar: not really sure if we'll fire beforematch or window.find fantasai> jarhar: at this point fantasai> jarhar: only motivation for me is to make easier to test across platform, but not aware of any use cases for window.find where you need to search hidden content astearns> ack smfr * Zakim sees TabAtkins, vmpstr on the speaker queue fantasai> smfr: ... fantasai> smfr: you select into it fantasai> smfr: and then you have to realize all the content * dholbert (~dholbert@ef89345d.public.cloak) has joined #css fantasai> smfr: seems like what you're proposing would bring in content during find, incremental improvement astearns> s/.../from what I remember about content-visibility/ fantasai> smfr: but wouldn't select content fantasai> smfr: previously if user did select-all on the document melanierichards> present+ fantasai> smfr: because of selection, would realize content for invisible stuff, woudl be slow (?) fantasai> smfr: but find would work in a reasonable way * Rossen_ (~Rossen@ef89345d.public.cloak) has joined #css fantasai> smfr: with this new thing fantasai> smfr: but why is find special? fantasai> smfr: what about scroll to text? fantasai> smfr: what about seach for addresses, metadata fantasai> smfr: #target fantasai> smfr: ... fantasai> smfr: Why only find? fantasai> jarhar: started with find, could expand to other use cases fantasai> levin: I think auto already supports all these things fantasai> levin: this is adjustment to 'hidden', which is not available to find-in-page astearns> ack TabAtkins * Zakim sees vmpstr on the speaker queue fantasai> TabAtkins: You mentioned how a page doesn't respond to the format event by revealing something, we'll remove their ability to do it fantasai> TabAtkins: does that mean we automatically turn the thing visible, or what? fantasai> jarhar: We stop firing the beforematch event fantasai> jarhar: it's invisible fantasai> TabAtkins: so the whole page is broken, not just one aspect of the use fantasai> jarhar: Usually there's some other way to reveal content in the page, just find-in-page would be broken * plh has quit ("Leaving") fantasai> TabAtkins: Before you'd walk up and try to find something auto that could fail open rather than failing closed fantasai> myles: So if you catch an event and do nothing, different from not catching the event?? fantasai> florian: I don't think anyone said that fantasai> florian: question is if you don't respond to event, do you stay hidden or get auto-revealed fantasai> TabAtkins: Default being to reveal fantasai> TabAtkins: if you're responding on your own, would do CancelDefault fantasai> jarhar: There's no way to possibly have the browser build the content * dauwhe has quit (Ping timeout: 180 seconds) fantasai> jarhar: page is in control of the style, same as 'hidden' TabAtkins> s/CancelDefault/preventDefault()/ fantasai> jarhar: CSS property says this is hidden, and until the page reveals, it should stay hidden fantasai> jarhar: There's internal state in the thing, and when you search for it, it would unlock similar to 'auto' fantasai> jarhar: but direction we're going, page maintains state, and it has to remove the CSS property fantasai> TabAtkins: I would like to have more discussion about that fantasai> TabAtkins: feels backwards for bad pages fantasai> TabAtkins: broken JS astearns> ack vmpstr * Zakim sees no one on the speaker queue fantasai> levin: Use cases we're targetting here are things like wikipedia collapsed sections fantasai> levin: where there are already handlers that expand the content fantasai> levin: this would add that find-in-page can expand the content fantasai> levin: if there's an error fantasai> levin: It prevents pages from figuring out what character is type by incrementally constructing a DOM but never revealing that content fantasai> levin: somewhat possible now with scroll offsets, but they're visible always fantasai> levin: Content you're searching is visible fantasai> levin: would allow you to search content, and remains visible vmpstr> S/levin/vmpstr/ fantasai> TabAtkins: Framing of strict improvement over 'hidden' makes me a little happier fantasai> TabAtkins: but think there should be some discussion about whether that's the right way to go TabAtkins> s/'hidden'/'display:none'/ fantasai> +1 to Tab's concern fantasai> and to smfr's fantasai> astearns: Hearing some concerns around what happens when events break astearns: .. fantasai> astearns: Wonder if we should take this back to the issue and get more of the proposal fleshed out, and answer questions, then bring back on a regular call fantasai> astearns: Any other discussion? fantasai> fantasai: Just +1 to TabAtkins and smfr's questions and conerns
Thanks for the questions at TPAC!
@fantasai I agree the details element is an important use case, it was brought up in this issue: https://github.com/WICG/display-locking/issues/162
@emilio
@smfr You mentioned other use cases for beforematch than just find-in-page:
To recap, the proposal is to add a new keyword to content-visibility
, called hidden-matchable
which acts like hidden
but allows searchability (find-in-page and scroll-to-text) and the beforematch
event.
In addition to @josepharhar's comments, I want to address another common question: should the default behavior of the beforematch be to un-hide the content automatically?
We've considered this in the past, and I think it adds to the complexity of the feature. I don't think there is an elegant way to un-hide the element when it's hidden by a css property: we can't modify the style sheet since the rule may be coming from some large class applied to a number of elements; we can either 1. add a pseudo class to keep the 'expanded' state or 2. modify inline style (ie set the style attribute on the element).
For 1, we then need a way to clear the pseudo class in order to hide the element again: Element.clearPseudoClassX? We can also clear it automatically if we remove the hidden-matchable property which means to hide something the script has to remove and re-add the property, which isn't ergonomic.
For 2, I think modifying inline style is awkward, especially if the page is using the style attribute for its own purposes (ie appending a new value to an existing string).
I think that requiring script to remove the value as a response to beforematch is the simplest approach. It also allows script to modify any other styles (e.g. change the orientation of the arrow in the section heading to indicate that its expanded), which is a common use case already.
For the details element, I think we can add an attribute to opt-in the details element into the behavior that it would essentially apply content-visibility: hidden-matchable and have a default handler that effective would remove it. In other words, we can make the details element be searchable and expandable with one attribute, but I feel that it is a separate discussion
In today's meeting, I would like to discuss:
content-visibility
Related issue about representing the visible state of auto
explicitly: https://github.com/w3c/csswg-drafts/issues/5695
The CSS Working Group just discussed [css-contain-2] Proposal: content-visibility: hidden-matchable
.
Thanks for the discussion today!
@smfr could you elaborate on the things in browsers that index content of web pages for searching?
@alice, @smfr requested input from a11y - do you have any thoughts about hidden-matchable DOM being included or excluded from the tab order as mentioned in the irc log or any other a11y thoughts about content-visibility: hidden-matchable?
We went back and forth on this a while ago - we came to the conclusion that with the way assistive technologies (ATs) work today, it makes the most sense not to expose this content to ATs until it is visible on the page. This does mean that an AT-specific find in page feature will not be able to use the hidden-matchable functionality, unfortunately, but the alternative (exposing that content to AT, even with a "hidden" flag set) would mean that AT users would not benefit from the intended performance benefits of the feature, which are even more acute when using AT.
As far as excluding this content from the tab order goes... it seems obvious to me that we would not want hidden-matchable content in the tab order. Is there some reason why it's not?
We went back and forth on this a while ago - we came to the conclusion that with the way assistive technologies (ATs) work today, it makes the most sense not to expose this content to ATs until it is visible on the page.
This does mean that an AT-specific find in page feature will not be able to use the hidden-matchable functionality, unfortunately, but the alternative (exposing that content to AT, even with a "hidden" flag set) would mean that AT users would not benefit from the intended performance benefits of the feature, which are even more acute when using AT.
This might depend on the implementation. If each element were exposed fully to the Accessibility tree, I agree that it would negate the perf benefit, but… Hypothetically, what if a general “find text” API were exposed in such a way that the AT could leverage the same path as the UA? VoiceOver already offloads some of its search functionality (next heading, next table, etc.) to WebKit. Perhaps a similar path could be used here.
- Is “We” here the TAG? Googlers? Some other group?
Just me, talking with the folks working on this proposal.
- Are any of those discussions linkable?
I think https://github.com/WICG/display-locking/issues/102 is the relevant public thread.
Hypothetically, what if a general “find text” API were exposed in such a way that the AT could leverage the same path as the UA? VoiceOver already offloads some of its search functionality (next heading, next table, etc.) to WebKit. Perhaps a similar path could be used here.
This is why I noted "the way ATs work today" specifically. I agree, deeper integration into an AT's find-in-page mechanism would be ideal.
This is why I noted "the way ATs work today" specifically. I agree, deeper integration into an AT's find-in-page mechanism would be ideal.
I’m suggesting this new API proposal could (should) allow that. The web app does not need to know if the agent initiating the search is the UA, or if it’s an AT behind it. How the UA exposes that search API to the AT is an implementation detail.
I’m suggesting this new API proposal could (should) allow that. The web app does not need to know if the agent initiating the search is the UA, or if it’s an AT behind it. How the UA exposes that search API to the AT is an implementation detail.
I don't think we're disagreeing at all. I think we both agree that hidden-matchable
contents should not be exposed in full to ATs, and that if possible with that limitation, AT find in page should be able to access contents within hidden-matchable
trees.
In the last meeting, it sounded like there was concern over beforematch/hidden-matchable being available to other algorithms. I'd be happy to add support (fire beforematch) for these cases which I understand:
window.location.hash
changes from script have async scrolling)window.find
since we can try making the scrolling async since it isn't specced and has very low usageI’m suggesting this new API proposal could (should) allow that. The web app does not need to know if the agent initiating the search is the UA, or if it’s an AT behind it. How the UA exposes that search API to the AT is an implementation detail.
I agree, and I would be happy to say that the user agent should feel free to fire beforematch under hidden-matchable during behavior similar enough to the user intent in find-in-page.
/sub
After thinking some more about the element fragment case for beforematch, I think it could get complicated:
It seems like we probably won't be able to make beforematch for elementfragments work the same as it does for scrolltotextfragment/find-in-page, which could be worse than omitting beforematch for elementfragments.
In addition, are already capable of expanding content in response to changes to the elementfragment via the hashchange
event, and you can see that mobile wikipedia already expands specific content when navigating with an elementfragment: try navigating here in mobile device emulation mode and you will see that only the target section is expanded: https://en.m.wikipedia.org/wiki/COVID-19_pandemic#Prevention
The CSS Working Group just discussed [css-contain-2] Proposal: content-visibility: hidden-matchable
.
My plan is to have a quick discussion (10 min?) at the beginning of the next meeting focused on a yes/no answer to the “Is this worth pursuing” question. So please bring up any latent deal-breakers here.
Does the difference in show/hide behavior for various ways of scrolling to content have to be solved for this to be viable? Are there objections to ScrollToTextFragment that would make this moot? Is there anything not yet discussed or that I’ve glossed over that show a fundamental problem with this approach?
Let’s please argue through everything we can asynchronously here before the next meeting starts.
Thanks for the discussion!
My plan is to have a quick discussion (10 min?) at the beginning of the next meeting focused on a yes/no answer to the “Is this worth pursuing” question. So please bring up any latent deal-breakers here.
From what I could hear, there was no objection about exposing hidden content, it was just about which algorithms, accessibility, and other details. If anyone has an issue with the approach in general, please say so, otherwise we’ll just focus on the details.
If possible I’d like to discuss point 2 from this comment, which is whether it’s the right idea to integrate this feature with content-visibility, as the first agenda item next time.
Does the difference in show/hide behavior for various ways of scrolling to content have to be solved for this to be viable?
The consistency argument in favor of firing beforematch for element fragments is pretty strong. I agree we should plan on firing beforematch for these fragments. Let’s continue this discussion of how to do so in the parallel html issue: https://github.com/whatwg/html/issues/6040
- What are the "addresses" and "metadata" use cases you talked about?
I brought these up because I think adding CSS behaviors for a single browser feature like Find is odd. Find isn't necessarily the only way that UAs look for things in page content.
addresses: WebKit API has a way for clients to ask for all the identified bits of metadata in certain categories in the content (e.g. addresses, telephone numbers), and these can be matched eagerly.
metadata: Safari indexes locally the text content of pages that you visit for Spotlight (searching outside of the browser can offer up a link to a page that you have previously visited).
In both cases, page content is traversed via the renderer tree (i.e. taking styles like display:none
and visibility:hidden
into account). My issue with content-visibility: hidden-matchable
is that it focusses too narrowly on the Find use case, and not on other use cases like these which also involve searching page content, but more indirectly.
Secondarily, a question: UAs can't run their Find In Page logic in content-visibility: hidden
content without actually doing style resolution, because they need to know if the find result will end up inside display:none
or visibility:hidden
content. I think that implies that UAs would have to do some amount of render tree building in order to implement Find inside content-visibility: hidden-matchable
. Is that something you've considered?
Also how is content-visibility: hidden-matchable
expected to interact with UA features like Reader view? If the content is important enough to allow Finding, should it be included in Reader views?
In both cases, page content is traversed via the renderer tree (i.e. taking styles like
display:none
andvisibility:hidden
into account). My issue withcontent-visibility: hidden-matchable
is that it focusses too narrowly on the Find use case, and not on other use cases like these which also involve searching page content, but more indirectly.
Thank you for the examples. I agree that the spec should allow the UA to traverse/search through the hidden-matchable content to support these use cases.
Secondarily, a question: UAs can't run their Find In Page logic in
content-visibility: hidden
content without actually doing style resolution, because they need to know if the find result will end up insidedisplay:none
orvisibility:hidden
content. I think that implies that UAs would have to do some amount of render tree building in order to implement Find insidecontent-visibility: hidden-matchable
. Is that something you've considered?
That’s a good point. In our prototype in Blink, we do recalc style, build the layout tree and do some layout as well in order to support this feature, which is necessary, as you said.
Also how is
content-visibility: hidden-matchable
expected to interact with UA features like Reader view? If the content is important enough to allow Finding, should it be included in Reader views?
Reader view sounds similar to Spotlight indexing. So I think hidden-matchable content could also be exposed to Reader.
In terms of preparing for the CSSWG meeting today, based on comments here and various offline discussions, I think we have consensus that the answer to both of the following questions is "yes":
I hope we can resolve on the "yes" answers in the meeting.
Then there is:
Once we resolve on 1 and 2, I hope to gather feedback on 3 in the remainder of the 10 minutes. And as a quick reminder, there is some discussion of why we ended up proposing the hidden-matchable value at the top of this issue. (It doesn't cover an attribute alternative though.)
The CSS Working Group just discussed [css-contain-2] Proposal: content-visibility: hidden-matchable
.
One is content-visibility as I understand is optimization so UA doesn't hav e to build render tree. Issue with allowing finding inside is if UA on every page load tries to look for readerizable content it means every page load your hidden-matchable content will still need to be figured out enough to give right answer to indexing code. So you lose the optimization
The same problem already exists with content-visibility:auto, which should also be searchable. I think this is more of an issue with content-visibility in general rather than content-visibility:hidden-matchable.
Also how is content-visibility: hidden-matchable expected to interact with UA features like Reader view? If the content is important enough to allow Finding, should it be included in Reader views?
Reader view sounds similar to Spotlight indexing. So I think hidden-matchable content could also be exposed to Reader.
content-visibility:hidden-matchable should be considered a UA hint for features such as Reader mode, and leave it up to the UA to provide a performant implementation, just as it should for other features.
For concerns about the meaning of visibility of hidden-matchable, here is the intention:
auto: Content is shown to the user.
hidden-matchable: Content is not technically shown to the user visually (and as a result the UA need not spend time rendering it by default), but is conceptually part of the current view and is only not shown because it's inside of a UX pattern such as an accordion or their off-screen portions of a userland infinite list.
hidden: Content is in the DOM but is not part of the current view. The only reason it's in the DOM is for caching, offscreen style/layout measurement, or developer convenience.
Responding to this comment also:
smfr: Second, anybody who wrote browser code knows you have to think about is content hidden by style, hide from APIs not just visually. display:none does, visibility-hidden sometimes does. Now we have 2 more and I'm uncomfortable adding more variations to is this content available in this scenario
One additional point: my understanding from offline conversations with @smfr is that the discomfort mentioned above is not about implementation complexity. It's about what 'matchable' means in contexts like reader modes, and whether that is a complicated mental model for developers.
My concerns here are twofold:
The CSS Working Group just discussed [css-contain-2] Proposal: content-visibility: hidden-matchable
.
Regarding this point raised in the CSSWG discussion: "fantasai: Once concern is if the control should be in css or in markup. Might be good to ask TAG about that." We couldn't find clear guidance for whether accessibility features need to be in HTML markup or not. @alice, any thoughts as an expert?
The TAG review has finished and they are satisfied. They didn't raise any concerns about the points @josepharhar raised in this comment, so I think all of the points raised at the CSSWG meeting two weeks ago have been addressed satisfactorily. I think this should be enough to resolve on this new feature. Adding agenda+.
The CSS Working Group just discussed [css-contain-2] Proposal: content-visibility: hidden-matchable
.
Off-minutes, I mentioned that it might be possible to skip styling and layout when doing the search, which is something Simon said was bothering him.
The idea would be that the browser can search all the text inside (or more text than usual) inside the hidden-matchable subtree, and fire the event even if it is not 100% sure the match it found will still hold when considering the full layout. In that case, the event fires, the content is revealed, and search is restarted, possibly finding no match in the revealed content. For example, the layout might end up finding that the text had a 1px font-size, and the browser doesn't want to display this matches to the user, then search continue from there down below.
It is an optimization, the browser is still allowed to do layout and styling if it desires to. Basically, the comment was that browsers should maybe be free to have <100% precision as long as it keeps 100% recall (i.e. false positives are allowed, but false negatives are not, or should be pretty rare).
It is an optimization, the browser is still allowed to do layout and styling if it desires to. Basically, the comment was that browsers should maybe be free to have <100% precision as long as it keeps 100% recall (i.e. false positives are allowed, but false negatives are not, or should be pretty rare).
This allows Find to be less expensive. It does not deal with:
hidden-matching adds a new level of ambiguity about what content is "visible"
That's true. I think that's a useful distinction to make though, because I can't find another way to achieve this. Maybe we should work on examples of use cases though, I feel this discussion was not rooted in concrete cases, which tends to yield more questions than answers. Maybe there's something obvious that already would solve this and we didn't notice.
why Find gets special treatment, given the other ways content is exposed by UAs
Making find an unique exception is something I would argue is indeed wrong. In the ideal world, the provisional content would also display in the accessibility tree (even as a place-holder template). In a way, I think we want something that says "this is not visible to the user yet, but it should be searchable/scannable" (and the UA's find feature is only one way to do so). After that, user agents might have bugs, and initially limit this to only search if that is the only thing they have budget for, but I feel this would be wrong indeed.
What are other examples of things you believe would be useful to have this type of hidden content exposed to?
Reader detection can't use the same level of more efficient but less precise content traversal without making it appear buggy to users.
Probably true. But the question is "does the Reader mode works better today"? Right now, I assume that the Reader mode doesn't see hidden content at all. In this case, we are exposing new content to the Reader mode that it should see, possibly with reduced functionality. But if it was display:none
you wouldn't see this content at all, which arguably is a worse kind of buggy. I guess we should do a before/after. I am not familiar with how reader mode is implemented in each browser.
In the spirit of trying to talk about use cases, here is the one that got all of this started, mobile wikipedia:
![image](https://user-images.githubusercontent.com/364405/107413122-ab266100-6b10-11eb-9e2b-f3e9fd15b810.png) | ![image](https://user-images.githubusercontent.com/364405/107413163-b9747d00-6b10-11eb-9603-8f9419347221.png) |
This is done using the following piece of CSS:
As things stand today, the content in the sections is:
In this use case, I don't see how things would be worse if there was a toggle to say that "the content inside that display:none
element should be searchable. Making this text searchable is not very complicated for the website, because simply removing the display:none
fixes everything. So if the browser sends an onbeforematch event on the section, it can just add an "expanded-section" class and everything will work as expected.
The second use case I have in my mind is a Word or ePub reader. Usually, this type of document is split into "chunks of content" (like pages, or chapters) which can be layouted independently. But there is so much content that layout out everything would probably cause a visible slow down. So the trick is to load content on-demand when it becomes scrolled to. The issue is that this prevents searching this content (and, for accessible users, it prevents displaying the list of headings, etc...).
Here again, each of these chunks can be marked as "currently-hidden but should-be-scannable". Then when you search through the book, you find that a word you are looking for is located on page 356. So the browser sends an event asking for page 356 to be converted from "currently-hidden but should-be-scannable" to "visible". Removing display:none
might be sufficient to do that, but some other processing might be necessary to achieve this (maybe some work is performed only on demand, like for instance rehydrating visualization components like interactive graphs, or maybe some of the fine-grained styling requires additional data; if that rehydration is done with a framework, it might end up shaking the dom, which means that the browser-highlighted span might get shaken up, which is why the onbeforematch event is useful, because it enables the author to say when the element is ready for search and highlighting).
In this case, I don't think it is possible not to set display:none
on the many pages of the book if you want good performance. A reader view should probably add this content, but load it as it comes into view. This might require additional development from the reader view, but this seems reasonable to me. In many cases though, maybe the reader mode can just ignore the "hidden but searchable" mark?
Maybe also there should be a note or author requirement in the spec saying that using this attribute should not be done on the first chunk of content, so that the browser can apply proper heuristics wihtout having to consider "currently-hidden but should-be-scannable" content more than as chunks of text.
Are there other use cases that should be considered?
why Find gets special treatment, given the other ways content is exposed by UAs
Making find an unique exception is something I would argue is indeed wrong. In the ideal world, the provisional content would also display in the accessibility tree (even as a place-holder template). In a way, I think we want something that says "this is not visible to the user yet, but it should be searchable/scannable" (and the UA's find feature is only one way to do so). After that, user agents might have bugs, and initially limit this to only search if that is the only thing they have budget for, but I feel this would be wrong indeed.
We've already resolved not to make find special and expose this feature through all appropriate algorithms.
hidden-matching adds a new level of ambiguity about what content is "visible"
Maybe I'm being pedantic, but I don't think "ambiguity" is the right word here. There is definitely a new state to consider, but it's not ambiguous what it means.
That's true. I think that's a useful distinction to make though, because I can't find another way to achieve this. Maybe we should work on examples of use cases though, I feel this discussion was not rooted in concrete cases, which tends to yield more questions than answers. Maybe there's something obvious that already would solve this and we didn't notice.
Re: "I feel this discussion was not rooted in concrete cases": what do you mean exactly? There are real-world examples in the explainer and in this issue. Likewise multiple additional ones were mentioned in the discussion today.
Re: "I feel this discussion was not rooted in concrete cases": what do you mean exactly? There are real-world examples in the explainer and in this issue. Likewise multiple additional ones were mentioned in the discussion today.
Not the issue per se, but the discussion we had today in general (there was a lot of process drama, blame shifted around, and theoretical arguments that I didn't feel were aimed at finding a solution). I believe that, at this point, we should focus on whether we want to solve the presented use cases (I do) and whether there is another way to do this that makes more sense than what's on the table. I haven't heard any yet, and I would love people who have objections or after-thoughts to articulate precise proposals solving or hinting at solutions for these use cases, or point why they don't believe this is worth fixing given the cost of the current proposal, which I think is pretty solid.
Not the issue per se, but the discussion we had today in general (there was a lot of process drama, blame shifted around, and theoretical arguments that I didn't feel were aimed at finding a solution). I believe that, at this point, we should focus on whether we want to solve the presented use cases (I do) and whether there is another way to do this that makes more sense than what's on the table. I haven't heard any yet, and I would love people who have objections or after-thoughts to articulate precise proposals solving or hinting at solutions for these use cases, or point why they don't believe this is worth fixing given the cost of the current proposal, which I think is pretty solid.
Ok got it, thanks for clarifying!
One additional point I'd like to make (@astearns suggested I talk it through) is regarding potential risk to the platform if we get this wrong.
There was quite a bit of discussion today about how this feature touches some things that are very important parts of the web platform - in particular, progressive enhancement, how to express meaning in HTML elements, and how it relates to CSS. I agree that all of these things are very important, and I think it's more than fair to say all of us in this group also think so.
So what if this feature ships and we end up deciding it was not the right thing, and need to back it out or replace it with a better solution? What would break / how hard would it be to do this?
I think it would be quite easy: we'd just stop firing beforematch
, and treat content-visibility: hidden-matchable
as an alias of content-visibility: hidden
.
Sites could not possibly break due to this, in the sense of a broken display or javascript errors. Likewise it should also still be possible for users to find all the content they want, because sites can't rely on find-in-page or search engines to drive users to the content they want - the sites also have UX affordances such as buttons, scrolling widgets, or accordion icons that have click event listeners to show the hidden content.
The main problem for sites would be if they come to depend (in the sense of expecting it to work as part of their UX plan for users) more deeply on beforematch
and would be unhappy to lose the feature. If the feature needs to get backed out or replaced, and this sites-want-it problem is deemed important enough, then we'd need to have a replacement feature, fulfilling the same use cases, already implemented before the old one is removed, and give sites some time to migrate.
So one thing that comes to mind regarding this, and which might not have been considered (or if it was, the explainer doesn't seem to say...), and which would:
Why not, instead of adding a per-match beforematch
event which only fires on elements with a particular CSS property, we do a global pair of events like beforeprint
/ afterprint
(beforefind
/ afterfind
? Do we even need afterfind
?)
In those events, the page could unhide all the content that it wants findable (it could maybe even get the string to be found, perhaps? not sure). It seems like a simpler design, and similar to how pages adapt to print in other ways other than CSS.
My reasoning for why this is likely better, even without the search string, is that all browsers have find-as-you-type kind of functionality, so in practice unless I'm missing something, beforematch is likely to actually match large parts of the document for find-in-page, when the user starts typing (and thus most of the content would be findable).
Getting the string to be found is perhaps more appealing in the scroll-to-text-fragment use-case, though I assume that the way this feature has been designed is to prevent exposing the exact fragment, which would have privacy implications. But then again the page can observe what the user has searched for with beforematch
, right? And anyhow, I don't think getting the query string is necessary, and it'd likely be better.
@chrishtr I wonder if such a solution was considered and if so why was it discarded? At least for find-in-page, it seems like a much simpler solution.
Why not, instead of adding a per-match
beforematch
event which only fires on elements with a particular CSS property, we do a global pair of events likebeforeprint
/afterprint
(beforefind
/afterfind
? Do we even needafterfind
?)In those events, the page could unhide all the content that it wants findable (it could maybe even get the string to be found, perhaps? not sure). It seems like a simpler design, and similar to how pages adapt to print in other ways other than CSS.
I think your idea is: batch-unhide all the things and render them all at once whenever any of these algorithms run. And then once the find-in-page or scroll-to-fragment operation is done, leave things in the opened state. Is that correct?
I'm not sure this works better. Here are reasons I can think of:
Performance: requires full rendering of everything. This would mean in particular page loads that use scroll-to-fragment would get a lot slower. Right now, Chromium's implementation only has to do style recalc (and even then it can be optimized further in the future).
Undesirable UX: sites may not want all sections or infinite-list content to be shown at once. For example, mobile Wikipedia would be much slower to scroll through (more gestures required) if all sections were opened at once, which is what would happen if you load the page with scroll-to-text or had just done a find-in-page. Or sites might have a UX that makes no sense to be "all open" - e.g. they have content in modal-like widgets.
My reasoning for why this is likely better, even without the search string, is that all browsers have find-as-you-type kind of functionality, so in practice unless I'm missing something, beforematch is likely to actually match large parts of the document for find-in-page, when the user starts typing (and thus most of the content would be findable).
The point you make about lots of beforematch
events firing has been raised before, in particular in the TAG review. I think a good way to solve it is to not fire the event on every keystroke, but instead at a slower rate, and/or only fire if there are enough characters typed to narrow the match sufficiently.
@chrishtr I wonder if such a solution was considered and if so why was it discarded? At least for find-in-page, it seems like a much simpler solution.
No, what you propose here hasn't been considered as yet.
Why not, instead of adding a per-match
beforematch
event which only fires on elements with a particular CSS property, we do a global pair of events likebeforeprint
/afterprint
(beforefind
/afterfind
? Do we even needafterfind
?) In those events, the page could unhide all the content that it wants findable (it could maybe even get the string to be found, perhaps? not sure). It seems like a simpler design, and similar to how pages adapt to print in other ways other than CSS.I think your idea is: batch-unhide all the things and render them all at once whenever any of these algorithms run. And then once the find-in-page or scroll-to-fragment operation is done, leave things in the opened state. Is that correct?
Sorta. If the page doesn't have access to the search string / fragment directive / etc, then yes, it can only unhide ~everything and pray. But if it does have access to it (which might be a reasonable thing...) then the page can decide what to show or what not.
Undesirable UX: sites may not want all sections or infinite-list content to be shown at once. [...] Or sites might have a UX that makes no sense to be "all open" - e.g. they have content in modal-like widgets.
Right, but those are also existing issues (for find-in-page, at least) with this proposal, right? As in, that's what happens if you do a find in page and type e.g. the "a" character with beforematch, right? (Assuming nearly all sections of a wikipedia article are going to have an "a" character around). The modal widget thing has also the same issue with beforematch
.
With the search string exposed as part of the event, pages could decide which thing to show.
Sorta. If the page doesn't have access to the search string / fragment directive / etc, then yes, it can only unhide ~everything and pray. But if it does have access to it (which might be a reasonable thing...) then the page can decide what to show or what not.
Are you suggesting then to expose the find-in-page or scroll fragment text to the page? This could be a privacy problem.
Undesirable UX: sites may not want all sections or infinite-list content to be shown at once. [...] Or sites might have a UX that makes no sense to be "all open" - e.g. they have content in modal-like widgets.
Right, but those are also existing issues (for find-in-page, at least) with this proposal, right? As in, that's what happens if you do a find in page and type e.g. the "a" character with beforematch, right? (Assuming nearly all sections of a wikipedia article are going to have an "a" character around). The modal widget thing has also the same issue with
beforematch
.
This is what the mitigation I mentioned is about. It would not fire the event if you type "a" if there are too many matches / "a" is too short of a string / it's been too little time since you typed another character. This would be something that is up to the UA to optimize.
With the search string exposed as part of the event, pages could decide which thing to show.
Wouldn't this mean the site has to implement search themselves? Or would they use window.find
(which we'd then write a spec for)?
This also won't work for find-in-page unless there was an event provided to script to listen in on what is being typed.
It's true that if you directly expose the find-in-page or search query text to script, then the script can be smarter about things. It can even do stuff like bringing in "search results" that are not even in the DOM at all (which I think is bad, for reasons of it not being good to keep semantic document state out of the document, as it messes up progressive rendering and accessibility goals in multiple ways). And as I mentioned there is a security / privacy problem.
A few of us (@smfr, @emilio, @fantasai, @fremycompany, @frivoal) got together to try and think a way out of the current apparent deadlock. Here's what we came up with.
Goals:
<details>
workDesign: UA does the reveal, sets an attribute so page can respond without JS, fires event to allow page to adapt if more than that is necessary.
Proposal: Add new value to 'hidden' attribute (e.g. hidden=until-found
) to represent hidden-matchable state, remove attribute when UA wants to reveal the content. It does not on its own hide the content; that's achieved by matching this attribute through selectors and applying display:none or content-visibility:hidden, as appropriate. (Default to ??? in the UA stylesheet.) It does mean though, that the content remains searchable even if it’s hidden away using something that would otherwise make it not searchable. Match triggers if element or content within is targeted (e.g. using find, using scrollToElement or .focus(), using fragID). In the case of search, only trigger on "find next", not merely when finding all the matches so that e.g. Wikipedia doesn't reveal all sections as soon as you type 's' into the search box.
Note: In WebKit, find normally requires layout, though a specialized path finding probable matches could conceivably be built.
ISSUE: Should sequential navigation ("go to next heading", tab navigation) trigger a match? Seems to depend on the use case.
P.S. We should also fire an event when beginning a search so the page can can update DOM with suspended content, but this is a separate issue.
Thanks for the proposal!
Improving <details>
sounds great!
Using a CSS attribute selector for hidden=until-found
which hides the content and letting the browser remove the attribute sounds great! It allows a wider variety of use cases to work without script.
I don't think that supporting find-in-page or other algorithms in display:none
makes sense since it can't have the style and layout resolution needed for them, but supporting visibility:hidden
and content-visibility:hidden
would be good.
Let's resolve on this at the F2F tomorrow so we can continue to make progress.
I would note that letting the browser remove the attribute isn't sufficient, because if the DOM is generated via tools like React, the attribute would reappear at the next React update. So, the onbeforematch
event is still required to support JS-driven websites (and other use cases, like rehydration of content).
So, given that, I propose that the default behavior (that is, if you don't call preventDefault
) of onbeforematch
would be to remove the hidden
attribute if its value allows it, and if the parent element is a <details>
tag, also set its open
attribute if it currently doesn't exist. Since the event tunnels and bubbles, that should work transparently, and authors can do something else if they would rather do.
The issue of how search would function on <details>
given its content is currently out of the tree while hidden (akin to display: none
) should be discussed separately. Maybe we can change the behavior of <details>
to rely on content-visibility:hidden for its content instead? Or we can provide a way to opt into this behavior, so as to not break compat.
There is still one thing which I don't like, and that is the "if you fail to unhide once, you never get the event anymore" because I don't see why it's useful to punish the page in this way. If calling onbeforematch
didn't reveal the content, you move to the next match and call it a day, I don't see why we want a persistant flag hidden to the page that disables the feature, that sounds like a footgun.
I was also wondering if the website could eventually return a Promise to the event to notify when the revealing has been done, so that the browser doesn't have to rely on heuristics like "the next frame". There are already precedents for this, as described on MDN. Of course, if waitUntil
is never called, the default timeout could be the next frame. That would indicate the website just let the browser remove the attribute, which will have an impact instantly.
There is still one thing which I don't like, and that is the "if you fail to unhide once, you never get the event anymore" because I don't see why it's useful to punish the page in this way. If calling
onbeforematch
didn't reveal the content, you move to the next match and call it a day, I don't see why we want a persistant flag hidden to the page that disables the feature, that sounds like a footgun.
The reason is to prevent the page from violating privacy by trying to read what you are typing into the search. However, it could be that this particular privacy mitigation doesn't end up being the best idea. How about we have spec text such as "the User Agent may skip calling page-defined event handlers if doing so is likely to lead to a privacy risk for the user. In these cases, content which would have been matched by the search but are currently not visible to the user are skipped."
I don't think we can support preventDefault
on beforematch
events because of how those are sent after a delay.
How is the new value for hidden content attribute supposed to work with hidden idl attribute which is defined to be boolean https://html.spec.whatwg.org/#htmlelement? Or is the idea that one can't use .hidden API to set the value to "until-found", only setAttribute. That would be a bit awkward and even error prone, I think.
I would like to propose a new value for the
content-visibility
CSS property:hidden-matchable
.content-visibility: hidden-matchable
would function the same ascontent-visibility: hidden
but also allow the browser to search for text inside the hidden content during find-in-page and ScrollToTextFragment. When used in conjunction with the proposed beforematch event, this would enable pages to reveal text in response to find-in-page before the browser scrolls and therefore make hidden sections which are searchable.An example usage of this feature would be to search the content of hidden, collapsed sections of mobile wikipedia, which are not currently searchable.
Non-hidden content is already searchable and so no event or CSS property is needed to expose it to user agent algorithms.
This is related to #3460, but is concerned with making hidden text searchable, rather than making visible text non-searchable.
Why
content-visibility
and not other “hidden content” mechanisms likedisplay: none
orvisibility: hidden
?Hidden content is usually put in
display:none
subtrees on sites today (for example, this is what mobile wikipedia does), to avoid rendering costs associated with the hidden content. However,display: none
content cannot actually be searched properly, because searching depends on certain features of styling to determine critical features such as word breaks, anddisplay: none
elements do not have CSS boxes.visibility: hidden
has several performance problems, such as being overridable in subtrees and not allowing the browser to avoid rendering work when hidden.The current values of content-visibility are in the css-contain-2 spec.
Sketch of edits to spec
'hidden-matchable': This value behaves very similarly to 'hidden'. That is, the element skips its contents. The skipped contents must not be accessible to user-agent features such as tab-order navigation, nor be selectable or focusable. However, unlike 'hidden', the skipped contents must be accessible to the find-in-page algorithm in order to allow the beforematch event to fire.