DEV Community

Cover image for Thoughts on find-in-page with custom accordion elements.
Anurag Hazra
Anurag Hazra

Posted on

Thoughts on find-in-page with custom accordion elements.

Today while scrolling the twitter timelines I stumble upon this interesting tweet by one of the google chrome engineer who is working on a pretty interesting problem on the internet which is being able to de-collapse accordions when user triggers a find-in-page action.

While as developers we sometimes miss these small details but after glancing over these problem you will realize how impactful these small things can be for better accessibility, discoverability & usability of a web app.

The Problem

The general problem is easy to understand,

Say you might want to search something on a page but you couldn't because the content which you are searching for is inside a collapsed accordion.

I've built and seen many accordion components but each and every one of them lacked this feature, and there's a good reason for it which we will get into later.

Research & implementation on the userland

After trying out few of the well known component libraries like Radix, ChakraUI.
I decided "well whatever, let's just implement this. How hard could it be?"

Oh boi, I was in for a interesting ride of 50+ browser tabs searching for solution.

So to implement this on userland, we need to do few things

  • Detect if user in find-in-page mode by detecting keypresses of CTRL+F
  • Record the user's search keyword
  • Match that keyword against all the accordion contents and de-collapse ones which matches the keyword.

Pretty simple right? Well NO!

Just even detecting if user is in find-in-page mode or the user closed the search modal is tricky.

To properly detect the event, we have to record and save all the events which is happening in a eventQueue

See, when user presses CTRL+F first thing what happens is window gets out of focus or in other words the blur event is triggered, With this we can detect if find-in-page modal is open if CTRL+F event and BLUR event happened subsequently.

Let's look at the code quickly:


const usePageFind = () => {
  const [isFinding, setIsFinding] = React.useState(false);
  const [eventQueue, setEventQueue] = React.useState<string[]>([]);

  React.useEffect(() => {
    window.addEventListener("keydown", (e) => {
      // detect CTRL+F if it passes then push to events queue.
      if (e.key.toLowerCase() === "f" && e.ctrlKey) {
        setEventQueue((p) => [...p, "CTRL+F"]);
      }
    });
    window.addEventListener("blur", () => {
      // push blur event to queue
      setEventQueue((p) => [...p, "BLUR"]);
    });
    window.addEventListener("focus", (e) => {
      // push blur event to queue
      setEventQueue((p) => [...p, "FOCUS"]);
    });
  }, []);

  React.useEffect(() => {
    const openSlice = eventQueue.slice(-2);
    const closeSlice = eventQueue.slice(-3);
    // if eventQueue's last 2 elements are CTRL+F & BLUR then we know the find modal is open
    if (arrayCompare(openSlice, ["CTRL+F", "BLUR"])) {
      setIsFinding(true);
      console.log("Finding open");
    }
    // if eventQueue's last 3 elements are CTRL+F, BLUR & FOCUS then we know the find modal is closed
    // We are checking for FOCUS because if user closes the find modal the page will be refocused again.
    if (arrayCompare(closeSlice, ["CTRL+F", "BLUR", "FOCUS"])) {
      setEventQueue([]);
      setIsFinding(false);
      console.log("Finding closed");
    }
  }, [eventQueue]);

  return { isFinding };
};
Enter fullscreen mode Exit fullscreen mode

And this is not even a perfect solution mind you.

Retrieving search keyword

But the real challenge here is detecting what user typed in the search field, because the window is blurred while user is searching we cannot hook into onKeyDown or any event handlers to know what user is typing.

But there is a very very very hacky trick which we can use to detect this, which I found while researching about this topic.

This article from Milan Laslop explained how the method works pretty well with implementation:
https://www.milanlaslop.dev/post/2020-01-11-javascript-detecting-what-the-user-searches-on-the-page/

I just implemented this on our code and lets see what the final code looks like:

NOTE: THIS IS VERY VERY BUGGY, AND ONLY A POC. DO NOT TRY THIS AT HOME.
Open this URL https://u8rtx.csb.app on a newtab for better experience

Thoughts

Now the above implementation which I created is not something I created to use in production nor it's a great solution, it's buggy, fragile, easy to break.

I solely created the example to show you how nearly impossible it is to build this without proper platform APIs.

Better Solutions

Next we will talk about possible naive solutions to this problem which you can use today & what new features are coming in HTML spec to improve & solve this problem.

Solution 1: De-collapse all the accordions on find-in-page trigger

A simple yet elegant solution would be to de-collapsing all the accordions in the page when we detect find-in-page event with our previously discussed usePageFind hook.

Solution 2: Use the platform

As mentioned in the original tweet which Joey Arhar is working on chrome 97 now supports auto expanding of the elements which you can use.

See live demo: https://auto-expanding-details.glitch.me/#target

With New APIs

Along with having built in support for this, since here we are talking about custom accordion elements we can also leverage new APIs which has been worked on namely:

  • hidden=until-found HTML attribute
  • and the beforematch event

These two together will enable us to build custom accordions with the same capability of text search in collapsed items.

Read hidden content spec to know how it works.

Conclusion

And that concludes my today's research on this interesting problem, I hope you learned something along the way.

It's amazing to see that browser are working on these type of features to improve the UX and the overall experience we have building websites which are generally more accessible.
Hope to see more features like this to land.

Without the new hidden content feature it's nearly impossible to build something like this.

Generally I would like to see all modern component libraries adopt these new platform patters to improve their components.

Links & glosarry

Top comments (5)

Collapse
 
grahamthedev profile image
GrahamTheDev • Edited

Really interesting but this misses a really important issue.

The page should not change unexpectedly and the second you start expanding accordions with CTRL + F as the trigger this will be unsettling for some people.

You certainly can't use that hack (even though it is super clever) as it doesn't work if you use the backspace key (and that is just one of the issues)!

So the answer is simple, use the native element and let browser vendors fix that or just don't worry about hidden content in accordions.

But I really want to fix this!

The best solution I can think of is to have a toggle <button> above an accordion that has "open all" and "close all" as an option.

That way people can open all the accordions and then search if they wish!

You could even have a little prompt pop up that asks "do you want to open all collapsed sections so your search results are better" if a user presses CTRL + F "yes, open all collapsed sections" and "no thank you" as options, but yet again that may be unexpected (but doesn't disrupt the page at least)!

The beauty of having the "open all" and "close all" option is that users who use the browser menu to search (as not everyone knows CTRL + F shortcut) can still use it ๐Ÿ‘

Anyway, that was quite a long way of saying I really enjoyed the article and have a much deserved โค and ๐Ÿฆ„! ๐Ÿคฃ

Collapse
 
anuraghazra profile image
Anurag Hazra

Hey thanks for the input, I'm glad you liked it.

So the answer is simple, use the native element and let browser vendors fix that or just don't worry about hidden content in accordions.

Yup absolutely.

You could even have a little prompt pop up that asks "do you want to open all collapsed sections so your search results are better" if a user presses CTRL + F "yes, open all collapsed sections" and "no thank you" as options, but yet again that may be unexpected (but doesn't disrupt the page at least)!

Ahh yes, that is probably a better and more UX friendly option.

The beauty of having the "open all" and "close all" option is that users who use the browser menu to search (as not everyone knows CTRL + F shortcut) can still use it ๐Ÿ‘

Yeah right or even combination of both of your approaches might also work.

Collapse
 
grahamthedev profile image
GrahamTheDev

At the end of the day they were just "off the top of my head", you might come up with something even better combining your ideas with those!

Once again, nicely written article! โค

Collapse
 
igor_bykov profile image
Igor Bykov

Really great article, thank you! ๐Ÿ‘
That's one of those things that (most of the time) will not naturally come into your mind when you think about a new feature.

However, since the current API seems to be really bleeding edge, I'd rather re-implement the native search (in the way, Confluence does, for instance) instead of hacking around a thing that cannot be controlled from the userland in the end of the day.

Once you can really control the input in the re-implemented Ctrl + F box, things become significantly easier.
For instance, one could lift accordion data structure up in the three & dispatch an event on the corresponding accordion each time the search query would match accordion's content.

Collapse
 
sonai95 profile image
Suvadeep Majumdar

Great article! specially the searched input retrieval was a tough nut to crack.
Great man