[5.3.2pre] Scroll binding

I have moved the post into a new topic as it might get lost as an update in the old thread

@jeremyruston: Did you ever implement this? Scrolling still feels a little choppy.

Also, if I change the tiddler the widget is bound to without recreating the widget (by closing and reopening its containing tiddler), the scrolling coordinates are not written into the new bound tiddler, e.g.

<$scrollable class='tc-scrollable-demo' bind={{{ [<now 0ss>addprefix[$:/temp/scroll/]] }}}>
<$list filter='[tag[Reference]]'>

<$view field='title'/>: <$list filter='[all[current]links[]sort[title]]' storyview='pop'>
<$link><$view field='title'/></$link>


This should write into a different bound tiddler for every second the clock shows when scrolling occurs (this is just for demonstration). But it always writes to the tiddler the widget was bound to when it was created.

Have a nice day

Thanks @Yaisog I’ve pushed fixes for both issues:

Hello @jeremyruston, great work (also with the scroll binding in general).

The example I gave above still does not work, though. After some thinking, I’m not sure that it can ever work. How would the $scrollable widget know that it needs to use a different tiddler for binding? It has no reason to check if its now argument has changed as a reaction to its content being scrolled. Interestingly, folding / unfolding also have no effect. I would have expected it to use a different bound tiddler after that.

Scrolling itself is much better, but when you’re used to modern smooth scrolling it still feels a little “viscous”. Would it be possible to wait for e.g. 100 ms after the last scroll event to write the coordinates to the tiddler, maybe as an option? I can’t see a use for writing multiple updates during a single scroll operation.
Due to the timeout when reading from the bound tiddler, even synchronous scrolling of e.g. two side-by-side rivers would not be really synchronous.

I’m sorry for this late feedback. I had the Talk topic open in a browser tab for weeks and weeks, but never got around to test it until it popped up again in the release notes.


For testing, I changed the code in scrollable.js like this:

		// Save scroll position on DOM scroll event
		var timeout;
		this.outerDomNode.addEventListener("scroll",function(event) {
			timeout = setTimeout(function() {
				var existingTiddler = self.wiki.getTiddler(self.scrollableBind),
					newTiddlerFields = {
						title: self.scrollableBind,
						"scroll-left": self.outerDomNode.scrollLeft.toString(),
						"scroll-top": self.outerDomNode.scrollTop.toString()
				if(!existingTiddler || (existingTiddler.fields["title"] !== newTiddlerFields["title"]) || (existingTiddler.fields["scroll-left"] !== newTiddlerFields["scroll-left"] || existingTiddler.fields["scroll-top"] !== newTiddlerFields["scroll-top"])) {
					self.wiki.addTiddler(new $tw.Tiddler(existingTiddler,newTiddlerFields));
			}, 100);

This kept scrolling smooth like a good whiskey and saved the position whenever I stopped scrolling. Which is exactly what I was looking for.

I’m not suggesting that this should be the default because I cannot know all use cases, but in my wiki I’ll make this custom change. I post it here for reference should anyone else be interested in keeping their scrolling buttery smooth.


Thanks @Yaisog funnily enough I’ve just coded up pretty much exactly what you’ve written. I do think we should make it be the default. We’re a long way from our refresh performance being good enough for animation effects, so I think we’re better off keeping it simple. We can always add further modes in the future.

1 Like

Hi @jeremyruston, looks good.

Regarding the refresh problem when changing the bind attribute, I think it’s cause lies in

ScrollableWidget.prototype.refresh = function(changedTiddlers) {
	var changedAttributes = this.computeAttributes();
	if(changedAttributes["class"]) {
		return true;
	if(changedAttributes.bind || changedTiddlers[this.getAttribute("bind")]) {
	return this.refreshChildren(changedTiddlers);

If the bind attribute changes (i.e. the bound tiddler title), we should not try to read the new scroll position from that tiddler, but start writing to that tiddler, especially if the new tiddler doesn’t exist yet. This would be done by calling refreshSelf because the eventListener must be updated? Only when the previously bound tiddler changes should the scroll position be read and updated (because then I’m modifying the coordinates by writing them into that tiddler).

My usage scenario is a single tiddler view that I would like to remember the scroll position for each tiddler when changing back and forth between them.


PS: I have a feeling this should be a GitHub discussion… :thinking:

Hi @jeremyruston, I created a PR where I tried to implement the necessary updating of the eventListener when the bind argument changes, instead of pasting a bunch of code here into Talk.

I hope it’s not too last minute-y.

One off-topic question: Would the scroll widget work vertically as well…
I meant horizontally… And horizontally and vertically in the same time?

Yes, the scrollable widget can handle both horizontal and vertical scrolling. Which directions scrolling can occur is determined by the size of the scrolled content compared to the container.