Page MenuHomePhabricator

[SPIKE 8hrs] Non-JS version of language selector opening first
Closed, ResolvedPublicSpike

Description

Background

In the QA of T280788, it was noticed that the non-JS version of the language selector was opening more frequently than expected.
e.g:

If I go back to a previous page then click forward, I initially get 1 column, and sometimes it closes immediately. On the second click, I will get multiple columns. I tried this with a more realistic scenario where I go to a page that links to the Paris page and click the Paris link to navigate to the page. I get the same initial single-column result.

https://phabricator.wikimedia.org/T280788#7067232

This task is to highlight any possible ways of minimizing the issue, and select the best possible way with minimal risk.

Acceptance criteria:

  • Recommend an approach for next steps and create a new task with the recommended approach

Event Timeline

Restricted Application changed the subtype of this task from "Task" to "Spike". · View Herald TranscriptMay 11 2021, 5:06 PM
ovasileva renamed this task from [SPIKE] Non-JS version of language selector opening first to [SPIKE 8hrs] Non-JS version of language selector opening first.May 11 2021, 5:08 PM
ovasileva updated the task description. (Show Details)

I've taken a bit of a deep dive into this language button loading behaviour. I'm breaking it down in terms of what happens when you click the button at each page state. I hope this table makes sense.

page stateJS stateClick language button
⚠️ Page is loading...⛔️ not loadedshow fallback menu
✅ Page has loaded⚠️ JS starts loadingshow fallback menu
✅ Page has loaded✅ JS loadedhide fallback menu, load ULS
✅ Page has loaded⚠️ ULS starts loadingdo nothing, wait until ULS loads…
✅ Page has loaded✅ ULS loadedShow ULS

These steps can be seen in the following video, where I load the page with debug mode turned on.

https://drive.google.com/file/d/18zF9zFsTl7FUZGvq5Ks5BjGnEyO7Y7me/view?usp=sharing

There I click the button while the page is loading, get the fallback menu, scroll it until the JS loads and hides it, click the language button again, wait for the ULS to appear, and close and open the menu again with the ULS loaded. This is with ?debug=true in the URL but without network throttling. It's a bit of an exaggeration since debug mode loads modules individually, but it still shows the total amount of code that has to be loaded (lots).

Open question: Currently, If someone clicks language button before the JS kicks in, and opens the fallback menu, should we then hide it after the JS loads? My view would be to let them continue using the no-JS version instead of hiding it and having them click the button again.

Nice to have: When someone clicks the language button with JS loaded, we don't provide any feedback that in fact the ULS is loading in the background and that may take a bit. We could for example, replace the language icon with a loading spinner while the ULS is loading to provide some user feedback.

Improving perceived performance: We could load the ULS on the button mouseenter event instead of just a click event, triggering the ULS loading as soon as someone hovers on the button.

I've taken a bit of a deep dive into this language button loading behaviour. I'm breaking it down in terms of what happens when you click the button at each page state. I hope this table makes sense.

page stateJS stateClick language button
⚠️ Page is loading...⛔️ not loadedshow fallback menu
✅ Page has loaded⚠️ JS starts loadingshow fallback menu
✅ Page has loaded✅ JS loadedhide fallback menu, load ULS
✅ Page has loaded⚠️ ULS starts loadingdo nothing, wait until ULS loads…
✅ Page has loaded✅ ULS loadedShow ULS

These steps can be seen in the following video, where I load the page with debug mode turned on.

https://drive.google.com/file/d/18zF9zFsTl7FUZGvq5Ks5BjGnEyO7Y7me/view?usp=sharing

There I click the button while the page is loading, get the fallback menu, scroll it until the JS loads and hides it, click the language button again, wait for the ULS to appear, and close and open the menu again with the ULS loaded. This is with ?debug=true in the URL but without network throttling. It's a bit of an exaggeration since debug mode loads modules individually, but it still shows the total amount of code that has to be loaded (lots).

Open question: Currently, If someone clicks language button before the JS kicks in, and opens the fallback menu, should we then hide it after the JS loads? My view would be to let them continue using the no-JS version instead of hiding it and having them click the button again.

Agreed - better the wrong menu than requiring an extra click.

Nice to have: When someone clicks the language button with JS loaded, we don't provide any feedback that in fact the ULS is loading in the background and that may take a bit. We could for example, replace the language icon with a loading spinner while the ULS is loading to provide some user feedback.

That sounds like a good idea. @alexhollender - any thoughts here?

So recapping our team conversation about this topic. We discussed two possible paths to improve the loading experience (or rather mitigate frustration) of the language button, as well as a few things we may be able to do to improve the perceived performance of the JS menu (i.e. ULS).


In terms of the overall loading experience, we can either:

1. Improve the fallback menu experience for JS users

Currently if someone is on a slow connection, they may get the fallback menu before the page loads and the JS kicks in. At that point, we hide the fallback menu, forcing them to click the language button again and load the ULS. We could instead not hide the fallback menu and let them use it. This is less intrusive than hiding the menu when someone may be using it, but it may provide a less consistent experience if someone gets the fallback menu sometimes, and the ULS menu other times (for example they're more likely to get the fallback menu on longer and more complicated pages with maps and charts).

or

2. Remove the fallback menu for JS users

Instead of showing the fallback menu on slow connection, we could instead replace the language button with a placeholder until the JS loads and the button is ready to use. We can do this because there is a small bit of code that executes on page-load and adds a client-js class to the page. This tells us that the JS will load eventually, and based on that we can assume that users will eventually get the ULS menu. This does means that we would remove the language switching functionality for most users until the page fully loads, however it's an open question if that's a real issue, since it largely depends on how quickly users reach for that button on page load (is language switching something users do immediately after page load?). This approach won't alleviate the subsequent loading of the ULS when clicking the language button, but it will signal to users when the button is ready to be clicked.


In terms of improving the perceived performance of the ULS menu itself, we can do a few things:

1. Add a loading indicator
When someone clicks the language button (when the initial JS is loaded) that triggers a subsequent loading of all the required ULS code. This is a substantial amount of code (50kb gzipped), and we currently don't provide any feedback that something is happening in the background. We could signal to the user that something is happening in the background with a loading spinner of some sort (maybe replace the language icon with a loading spinner while the ULS is loading?)

and/or

2. Load the ULS on mouse-enter instead of on click
This amounts to loading the ULS a tiny bit early, when the user hovers over the language button instead of when they click it. I'm not sure if this will have a meaningful impact, but it is something we can try.

One thing to note however, is that adding a spinner and loading the ULS on hover does increase the amount of state and complexity we have to manage, because I assume when someone hovers over button we don't want to show a spinner, since they haven't actively requested the menu to open yet, but when they click it, we do want to show a spinner? (this may be over-engineering... just a wee bit).

LGoto lowered the priority of this task from High to Medium.Jun 3 2021, 5:10 PM

I've taken a bit of a deep dive into this language button loading behavior. I'm breaking it down in terms of what happens when you click the button at each page state. I hope this table makes sense.

#page stateJS stateClick language button
1⚠️ Page is loading...⛔️ not loadedshow fallback menu
2✅ Page has loaded⚠️ JS starts loadingshow fallback menu
3✅ Page has loaded✅ JS loadedhide fallback menu, load ULS
4✅ Page has loaded⚠️ ULS starts loadingdo nothing, wait until ULS loads…
5✅ Page has loaded✅ ULS loadedShow ULS

@Jdrewniak thanks for spelling this out so clearly.

If I'm understanding correctly:

Page state #1 — we have no choice of what to do
Page state #2 — we know JS (and therefore ULS) will load
Page state #3 — we're waiting for ULS to load
Page state #4 — we're waiting for ULS to load
Page state #5 — we're all set

For page states 2, 3, and 4 would it be possible to show something like this (riffing off what Gmail does when it's loading):

image.png (255×360 px, 12 KB)

That way people know the language switcher is coming but have the chance to switch to the basic one.

Regarding the situation where the page state transitions from state 2 to state 3 while the language switcher is open, I agree that we shouldn't try to swap the menu. Would it be possible to give them the option inline, should they want the ULS (mainly because it has search)? Not sure this would be worth the effort, but might be helpful to know going forward.

image.png (564×360 px, 18 KB)

cc @Pginer-WMF.