A performance minded details widget variant

I’ve been trying to tune the performance of my wiki and there are some very “expensive” filters than run on some of my pages. I tried the standard html <details> functionality and while it looks good, my testing seems to indicate that the content within the details all gets run whether it shows or not. That would sometimes be useful in that expanding and contracting the content repeatedly wouldn’t re-run the filters, but for me I essentially only want the filters to process when I’ve clicked to expand. I looked through what I could find, and it seems most of the things posted here just had extra css on the standard functionality, so I wrote a quick widget to essentially make it look like details, but wrap it in some logic to store a value for hide/show so it won’t process unless asked to.

I tried to keep as close to standard as possible so instead of the standard:

<details><summary>My Caption</summary>
This is my content
</details>

This version is:

<$.details caption="My Caption">
This is my content
</$.details>

I used white arrows instead of black to add a tiny bit of visual differentiation so I can distinguish it. Again, the main purpose is to shift performance bottlenecks. You can also set your own ID so multiple things expand and contract (it’s based on currentTiddler right now as a fallback). It also seems to handle block content like bullet lists automatically (in the standard html I think you have to wrap with divs?)

{22B5982A-9D3E-4C8D-B628-D9154B22B865}

$__stobot_widgets_details.json (650 Bytes)

5 Likes

Yep, I have a feeling a few of us have come to the same conclusion: the HTML element is great for small chunks of content or content that doesn’t involve many calculations.

This is why telmiger’s Details Widget can also still be very useful.

I’ll throw in my code as an example of a more-verbose version:

\widget $br.details()
    \function widget.id() [<state>lowercase[]match[yes]then[$:/state/br/details/]else[$:/temp/br/details/]] [<counter>] [<currentTiddler>] [<label>] :and[join[]]
    \function widget.state() [<widget.id>get[text]match[show]then[hide]else[show]] :else[[show]]
    \procedure toggle-visible() <$action-setfield $tiddler=<<widget.id>> text=<<widget.state>> />
<$parameters summary="Details", details="", state="no", label="" icon="$:/core/images/down-arrow">
    <div class={{{ [[br-details ]addsuffix<widget.state>] }}}>
        <$button class="summary tc-btn-invisible" actions=<<toggle-visible>>>
            <span class={{{ [[summary-icon ]addsuffix<widget.state>] }}}><$transclude $tiddler=<<icon>> /></span>
            <div class="summary-content"><<summary>></div>
        </$button>
        <% if [<widget.id>get[text]match[show]] %>
            <div class="details-content"><<details>></div>
        <% endif %>
    </div>
    <style>{{$:/br/utilities/details/.styles}}</style>
</$parameters>
\end $br.details

<<$br.details   details:"some text to show&hellip;">>

<<$br.details   details:"some text to show&hellip;"   label:"widget-example2">>

There’s also some CSS styling involved that isn’t shown.

Seeing your work, I really should either:

  • try to simplify my code
  • just use yours and tweak it if needed. :sweat_smile:

I exclusively used telmiger’s solution for years, then played a bit with the Shiraz (subplugin) callout detail variant, but have recently been migrating (but not consistently) toward html details (with my own css classes).

Why did I revert to plain-html details? Besides trying to reduce plugin dependency, I found one of my workflows required lifting content from TiddlyWiki and pasting it into other html environments… And this was before we could get raw html in the preview pane (or before I knew how to do that). So that’s no longer a factor, but I do have all this actual content in html details now…

Anyway, I’m very eager to think through a best-practices approach, addressing the performance concerns raised by @stobot

A couple considerations from my experience with plain-old-html details:

  • :+1: Plain-old-html details approach allows markup, including links and wikitext, within the summary, which I end up using more than I would have expected. For example, this works: <summary> More details below on <$count filter={{!!filter}}/> items under tag <<tag>> </summary> !
  • :+1: It’s easy to target the plain-html summary itself (as well as the overall details tag) “on the fly” for a class or style declaration.
  • :-1: BUT… plain-old html details often fails to render block mode properly (and hence messes up wikitext such as lists and tables) unless I explicitly bundle all of the enclosed contents inside another set of <div> tags. I appreciate that telmiger’s widget — and probably yours? — sidesteps this nuisance.

Ah, I’ve just tried it… In addition to this small stylistic difference, your widget (unlike telmiger’s version) actually doesn’t work with html details at all in the render process. The raw-html preview for

<$.details caption="My Caption">
This is my content
</$.details>

shows as…

<button class="tc-btn-invisible">▷︎ My Caption
</button>

I’m guessing that’s what you had to do in order to bypass the performance problem! But it does mean that the html coming out of your widget is less flexible/portable.

Perhaps your solution could include some kind of action that would render during certain html-export processes so that the content would behave a bit better (albeit with the performance limitations) if ported beyond TiddlyWiki…?

Yes, I liked the way that <details> worked, but needed the performance workaround, so I used a TiddlyWiki custom widget to build something. I personally have no need for something that can “port beyond TiddlyWiki” or know how to adjust for such a need, sorry. If you figure it out, feel free to post a better version back here!

1 Like

Well, I’m unlikely to need the performance-maximizing version myself, but here’s a thought about maximizing compatibility:

What if your button-click added your content (as it does now) but framed within an html details element, in open state, with css class that — in your bundled package — makes the summary part display:none; (because your button has got the summary)…?

Then whenever the [[$:/temp/details/]addsuffix<id>] tiddler(s) are set to “yes”, the exported html would include a stray button (no big deal) where your proprietary open-close toggle lives, but would also show an html details element. (And since exports easily leave css behind, the html details would render in the usual basic way.)

Presumably the extra html details frame would affect your interface not at all, and would also have negligible impact on performance, yes?

One more observation: I often use multiple details disclosures within a single tiddler. Your solution seems to require that all of them expand/collapse at once, because the naming of the temp tiddler is a simple variation on the tiddler name. A parameter for an additional index value would enable multiple independently-acting buttons within the same tiddler, yes?

I apologize that my level of fluency with certain aspects of widget design (such as use of <$slot $name="ts-raw"/>) is too rough for me to mock up the actual changes I’m imagining… I’m at that journeyman level: it feels like I know my way around what TiddlyWiki can do, but not always around the details (see what I did there? :wink: ) of how.

This sounds like a standard $button and $reveal would do the trick. Something like this:

<$button class=tc-btn-invisible set="!!statefield" setTo={{{ [{!!statefield}toggle[show],[]] }}}>
   <$let arrow={{{ [{!!statefield}match[show]then[&#x25BD;]else[&#x25B7;]] }}}><<arrow>></$let>
   My Caption
</$button>
<$reveal state="!!statefield" type="match" text="show" tag=div>
   This is my content
</$reveal>

Notes:

  • The $button toggles a statefield (in the current tiddler). If the field is missing or the field value is blank, it sets it to “show”, otherwise it sets it to blank
  • The $let...<<arrow>>.../$let shows either a “down pointing white triangle” (unicode 25BD) or a “right pointing white triangle” (unicode 25B7)
  • The $reveal renders your content, but ONLY when the statefield value is “show”
    • If you DO want the content rendered all the time, you can add a retain=yes parameter to the $reveal widget
    • If your content uses “block mode” wikitext (such as bullets or tables), you need a blank line at the start of the content

enjoy,
-e

Thanks Eric, it’s always nice to see other ways of doing things! I’ve always appreciated your walkthroughs as I learn a lot from them.

I didn’t have a problem to solve in this case, I had walked through my situation that caused me to create what I had shared. I did essentially the same thing using list instead of reveal. :+1:

Understood👍. Sounds like there are some features you don’t need that I did - and vice versa. Rather than create something a little “bloated” that solves both, I’ll keep mine minimal, and I’ll celebrate if someone comes up with another version that solves a related issue they’re having. Some of the things I create I do as a formal plugin that are meant for wide audiences with lots of documentation etc. Because there are so many other versions of details (including telmiger’s as you noted), I didn’t intend to go down this path, just have something niche in the forums for people to stumble upon in case others are trying to solve what I was.

Yes, it’s a little buried at the end of the post, but as mentioned you can set your own ID. I didn’t show that (just referenced it), but you can provide the parameter id just like:

<$.details id="my-id" caption="My Caption">
This is my content
</$.details>

The <<currentTiddler>> is just used as a fallback in case none is provided, which works for my needs. I might as well note also that you also don’t need to specify caption, there’s a default for that as well.

Thanks for the feedback!

1 Like