Javascript in Tiddlywiki

With a little searching I came across this, I had to rework it a little, and it is incomplete but it demonstrates a CSS only solution.

<style>
/* ''Collapsing/Expanding Nested Lists'' */
/**/
div > * > * > ul li:hover { color: red; }

div > * > * > ul ul li {
display: none;
}
div > * > * > ul:hover ul li {
display: list-item;
}
div > * > * > ul > ul:before {
content: "more...";
margin-left: -2em;
border: 1px solid #bbbbbb;
padding: 0.1em 0.2em 0.1em 0.2em;
font-size: 0.9em;
color: #999999;
background-color: #eeeeee;
}
div > * > * > ul:hover > ul:before {
display: none;
}
/**/
</style>

<div>

* #1
** #1-1
*** #1-1-1
* #2
** #2-1
*** #2-1-1
*** #2-1-2
** #2-2
*** #2-2-1
*** #2-2-2
</div>
1 Like

This is quite interesting. Thank you for this!

Is there any way to (a) make this applicable to each level as opposed to just the top, and (b) make it clickable as opposed to just hovering? From my understanding, this would require JS but I may be mistaken.

It should be doable with CSS only,

however I am no expert. Hopefully someone else with good CSS skills can extend this.

Thanks for helping out with this! It’s unfortunate Tiddlywiki isn’t able to handle my need very well with this, but alas!

Give this a try at TiddlyWiki.com:

\define li(indent tiddler)
<details style="margin-left:calc(15px * $indent$);margin-top:-10px;"><summary><$text text="$text$"/></summary>
<$tiddler tiddler="$text$">
<$transclude mode="block">
</$tiddler>
</details>
\end

<<li 0 "HelloThere">>
<<li 1 "HelloThere">>

I find this kind of programming in TiddlyWiki way more fun and interesting than javascript.

Alternatively, you could replace the details element with list items wrapped in buttons, those buttons used with reveal widgets. Try this to see how it looks:

* <$button class="tc-btn-invisible">item</$button><br><$reveal>...</$reveal>
* <$button class="tc-btn-invisible">item</$button>

you can put your javascript into a widget - there is a little bit of boiler-plate -
put the code below into a tiddler with fields ‘type’ = ‘application/javascript’ and ‘module-type’ = ‘widget’, the name of the tiddler is not important.

(function(){

/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";

var Widget = require("$:/core/modules/widgets/widget.js").widget;

var meunOpenWidget = function(parseTreeNode,options) {
	this.initialise(parseTreeNode,options);
};

/*
Inherit from the base widget class
*/
meunOpenWidget.prototype = new Widget();

/*
Render this widget into the DOM
*/
meunOpenWidget.prototype.render = function(parent,nextSibling) {
	this.computeAttributes();
	this.execute();
};

/*
Compute the internal state of the widget
*/
meunOpenWidget.prototype.execute = function() {
};

/*
Refresh the widget by ensuring our attributes are up to date
*/
meunOpenWidget.prototype.refresh = function(changedTiddlers) {
	return true;
};

/*
Invoke the action associated with this widget
*/
meunOpenWidget.prototype.invokeAction = function(triggeringWidget,event) {
/************************* your code **********************/

  const listItem = event.target.closest('li');
  console.log("called",event)
  if (listItem) {
    const childList = listItem.querySelector('ul');
    
    if (childList) {
     listItem.classList.toggle('open');
    }
 }

/************************************************************/
return true; // Action was invoked
};

exports["action-menuopen"] = meunOpenWidget;

})();

Then in another (standard) tiddler goes the html and css:

\define clickactions()
<$action-menuopen />
\end

<style>
.collapsible-list ul {
  display: none;
}

.collapsible-list .open > ul {
  display: block;
}

.collapsible-list li {
  cursor: pointer;
}

.collapsible-list ul ul {
  margin-left: 20px;
}
</style>

<$eventcatcher $click=<<clickactions>> >

<ul class="collapsible-list">
  <li>(Click Me) Bullet Point 1
    <ul>
      <li>Sub Bullet Point 1.1
        <ul>
          <li>Sub Sub Bullet Point 1.1.1</li>
          <li>Sub Sub Bullet Point 1.1.2</li>
        </ul>
      </li>
      <li>Sub Bullet Point 1.2</li>
    </ul>
  </li>
  <li>Main Bullet Point 2
    <ul>
      <li>Sub Bullet Point 2.1
        <ul>
          <li>Sub Sub Bullet Point 2.1.1</li>
          <li>Sub Sub Bullet Point 2.1.2
            <ul>
              <li>Sub Sub Sub Bullet Point 2.1.2.1</li>
              <li>Sub Sub Sub Bullet Point 2.1.2.2</li>
            </ul>
          </li>
        </ul>
      </li>
      <li>Sub Bullet Point 2.2</li>
    </ul>
  </li>
</ul>

</$eventcatcher>
3 Likes

This looks interesting @buggyj .Is there any way to make use of this in regular tiddlers without using the html and css code wrapped around tiddler text within the same tiddler. I meant can the css and html code be moved to separate tiddlers and still be able to get the collapsible outline structure for wikitext bullet formatting (may be by using a class field or tag). That would be interesting for note taking.

This resembles the Outline plug in obsidian (I guess that’s what the OP also wanted)

Nice idea implementing it as an actionwidget and using eventcatcher. Here is a share site link containing @buggyj’s code for anyone who wants to try it without having to do the work of creating the tiddlers (and reloading the wiki to get the javascript to take effect).

Super interesting! Thank you so much for this. Very cool.

I can definitely work with this.

Following @arunnbabu81 question, is there any way to make use of this without needing the html/css?

yes you could reduction the risk of ‘chevron blindness’ by rewriting the wikitext like so (the css is not needed as such)"

<$eventcatcher $click="<$action-menuopen/>" >

@@.collapsible-list
*(Click Me) Bullet Point 1
**Sub Bullet Point 1.1
***Sub Sub Bullet Point 1.1.1
***Sub Sub Bullet Point 1.1.2
**Sub Bullet Point 1.2
*Main Bullet Point 2
**Sub Bullet Point 2.1
***Sub Sub Bullet Point 2.1.1
***Sub Sub Bullet Point 2.1.2
****Sub Sub Sub Bullet Point 2.1.2.1
****Sub Sub Sub Bullet Point 2.1.2.2
**Sub Bullet Point 2.2
@@
</$eventcatcher>

A feature of this approach is the state of open element of the list is not maintained - if you close the tiddler and reopen it the list will be completely closed.

I wonder if we can slip in a child indicator? So we know when to click.

I am trying to package this but when I move the style out of the tiddler to one tagged $:/tags/Stylesheet is stops working, Odd?

It works for me.

with chrome you can do this with the css


.collapsible-list ul {
  display: none;
  position: relative;
  list-style: none;
}
.collapsible-list {
  list-style: none;
}

.collapsible-list .open > ul {
  display: block;
  position: relative;
  list-style: none;
}

.collapsible-list  li:not(:has(ul)) {
cursor: default;

}

.collapsible-list  li:not(:has(ul)) {
cursor: default;

}
.collapsible-list  li:has(ul)::before {
  content: '⯈';

}
.collapsible-list  li.open:has(ul)::before {
  content: ' ⯆';

}
.collapsible-list  li:not(:has(ul))::before {
  content: '⏺';

}


.collapsible-list ul ul {
  margin-left: 20px;
}

but not yet on firefox

See this wiki.. I tried to create a view toolbar button to switch to a custom View Template Body Cascade to make the bullets collapsible. But its not working - check this demo. Press on this image button
in the viewtoolbar to switch to the custom View Template Body Cascade. This is the template I used for the cascade. All the relevant tiddlers are listed in the homepage. Can someone debug it ?

Check in chrome only. I have added the javascript widget and CSS shared by buggyj already and its working outside my View Template Body Cascade - here is the demo

The problem is that this styling in wikitext does not always work, eg:

@@.collapsible-list
{{listdynamicexpample}}
@@

You cannot use transclusion here. The only solution is to use javascript macro.

Is it possible to use javascript macro with the view template body cascade ?

They can be used where wikitext macros are used. Generally the javascript one uses in a macro is very simple.

here is the macro I wrote for this:

/*\
title: bj/macros/stylethis.js
type: application/javascript
module-type: macro
\*/
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";

/**** name of macro and parameters ****/

exports.name = "styleThis";

exports.params = [
	{name: "aclass"},
	{name: "tid"}
];

/***************macro********************/
exports.run = function(aclass, tid) {
var content = $tw.wiki.getTiddlerText(tid);
return `
@@.${aclass}
${content}
@@
`
};
/***************************************/

you use it like this:

use it like this:

<$eventcatcher $click="<$action-menuopen/>" >

<$macrocall $name="styleThis" aclass= ".collapsible-list" tid=<<currentTiddler>>/>

</$eventcatcher>
1 Like

Wow. Thanks @buggyj . It works perfectly now. I have updated the demo wiki including the latest code you shared.

@arunnbabu81 forgive me for my ignorance, but how can I use this macro?

@switchplayer
I will share the tiddlers needed today night once I am back home. Everything except the icon for the viewtoolbar button is in the home page of that wiki…you can try those.the icon tiddlers can be searched in the wiki using command palette plug in (control plus P). I forgot it’s title. Title can be seen within the button tiddler