0

I'm trying to implement a simple tabs component in a single-page app. The selected tab should (obviously) display its contents while unselected tabs keep their content available but hidden. How should I hide the content on the unselected tabs?

Each of the canonical techniques for hiding content has issues:

technique drawback
opacity: 0 User can still potentially interact with invisible content. Breaks if a sub-element of the tab element sets the opacity property.
color: transparent, background: transparent Same issues as above.
display: none Component on unselected tabs is loaded in a div with no dimensions, which in my experience causes rendering issues when that content is later displayed. (Several React libraries I'm using do not properly calculate inner dimensions of columns or whatnot when they're initially rendered in a display: none div and later displayed.)
visibility: hidden Still takes up room on the page. Breaks if some sub-element of the tab element sets the visibility property.

To sum up: I want to know how to render content as if it's actually loading on the page (i.e., with the proper dimensions), but completely invisibly, with no space reserved for it on the page and with no possibility of user interaction. Ideally, the solution should be agnostic to whatever CSS properties are set on the arbitrary component within the tab itself; i.e., the CSS inside the tab content should not be able to break the display of the tabs themselves. Is there some recommended combination of CSS properties (visibility, opacity, display, position, z-index, etc.) that does what I want here?

Sasgorilla
  • 2,403
  • 2
  • 29
  • 56
  • combine visibility and opacity. It is widely used in youtube tutorials – KasRoudra May 02 '22 at 16:17
  • If the only issue you have with `opacity: 0` is, that the user could interact with invisible content, you could maybe use `pointer-events: none` on it – Apollo79 May 02 '22 at 16:19
  • 2
    There's an additional consideration you're missing here-- content that is merely visually hidden may still be accessible by keyboard or screen readers. If you're going to keep it on the page but hidden you're going to have to additional work to make sure it isn't going to negatively impact the accessibility of the experience. Doing DOM investigation is usually to be avoided in React expect under certain circumstances-- are you sure this is necessary? Or would forcing re-renders be a better approach perhaps? – Alexander Nied May 02 '22 at 16:27
  • *This question is likely to be answered with opinions rather than facts and citations. It should be updated so it will lead to fact-based answers.* – The Fool May 02 '22 at 16:33
  • The only way I see to accomplish all requirements is hiding the component behind another components (z-index) or putting it outside of the visible screen (position), though there will be scrollbar issues. – Onki Hara May 02 '22 at 16:33
  • `unselected tabs keep their content available but hidden` Hmm I'm wondering why you need the content to be available in DOM but hidden. If you're using React, you can set the state for conditional renderings instead of handing with CSS – Nick Vu May 02 '22 at 16:33
  • @NickVu the main reason for that is to do API calls on the hidden tabs as quickly as possible on page load; I don't want to wait until the user clicks the tab before loading all the content. Even after initial load, I believe mounting and unmounting components may be expensive for complicated components and will degrade performance of switching between tabs, though I might be wrong about that. But this might still be something to explore further. – Sasgorilla May 02 '22 at 16:42
  • 1
    @Sasgorilla in my opinion, you can achieve the same thing, if you call all APIs for tab data on the tab container component and pass them down to the tab components. The nature of showing/hiding tabs with renderings won't affect the data you already keep in the tab container component. – Nick Vu May 02 '22 at 16:52
  • Does this answer your question? [Hide Show content-list with only CSS, no javascript used](https://stackoverflow.com/questions/17731457/hide-show-content-list-with-only-css-no-javascript-used) – RedRex May 02 '22 at 16:52
  • @NickVu My tabs component is intended to be standablone and reusable, so I don't want to keep state on the specific tabs there. There may be a solution where I keep the state in a custom controller component which uses the tabs. However, my app allows different tabs to open other tabs in a flexible way, and managing all the state of all open tabs at one controller level, instead of letting each tab worry about its own independent state, would be quite difficult. I agree though it's something to think more deeply about, however. – Sasgorilla May 02 '22 at 16:55
  • I agree with @NickVu -- this seems likely to be an XY problem, and I think your solution is probably to lift state and treat the shared container as an orchestrator that makes all these fetches on behalf of the child tabs and passes the responses down. I'm aware this might involve relatively involved refactoring, but to me it certainly seems a preferable alternative. – Alexander Nied May 02 '22 at 16:57
  • 1
    `managing all the state of all open tabs at one controller level, instead of letting each tab worry about its own independent state` as you stated, you already have a tab controller which means you already fetch data from the controller level and pass data down to tabs? I'm kind of confused from your statement though @Sasgorilla – Nick Vu May 02 '22 at 17:04
  • @NickVu The tab level (tab controller) purely manages opening and closing tabs (and a few other tricks like renaming, reordering, etc.). It knows nothing of the content of the tabs or the business logic of the app; all it knows (essentially) is a list of tab titles and a corresponding `JSX.Element`. Each tab itself is a React component which manages state and makes its own API calls. (Some of those tabs actually share some state through context but that's orthogonal to the central design problem.) – Sasgorilla May 02 '22 at 17:20
  • 2
    Hmm seemingly your situation is tough. The workaround I can think of is using `display: none` and trying to set some `min-height` as height prediction on tab content. Otherwise, you need to lift your API calls to the tab controller as the trade-off. By the way, other people may have a complete solution for this, let's wait for their answers :D @Sasgorilla – Nick Vu May 02 '22 at 17:38
  • 1
    It _might_ be worth opening a separate question that includes some of this detail from the comments and asks the more general question of how to manage this design pattern. – Alexander Nied May 02 '22 at 17:43

0 Answers0