Tiddlywiki5: [IDEA] Add a generic event trapping widget

Created on 19 Nov 2020  路  13Comments  路  Source: Jermolene/TiddlyWiki5

In a project I'm working on I need to display tens of thousands of relatively small, simple tiddlers in a container DIV. Users need to be able to click on a tiddler to perform actions on it. The simplest solution is to wrap each tiddler with a button or link widget, but doing so has a significant impact on rendering performance. It is proposed to add a new event trapping widget that caters for this use case and related situations. There was some discussion about the idea here too.

The code for the prototype <$event> widget is here:

https://gist.github.com/Jermolene/663bc8e96285e7eea7987f58ff6ad948

Here's the event handler which demonstrates the essential features:

  • Only handles events whose originating DOM node is contained within a DOM node that matches the provided selector (so .myclass will match events originating from a DOM node with the class "myclass")
  • The attributes of the selected DOM node are made available to action strings as variables prefixed dom-
  • I haven't yet needed to, but the idea would also be to copy all the useful properties from the event object into attributes prefixed event-

    • We might want to make special cases for the modifier keys and mouse buttons for consistency with existing event handlers

function(event) {
    var selector = self.getAttribute("selector"),
        actions = self.getAttribute("actions"),
        selectedNode = event.target,
        variables = {};
    if(selector) {
        // Search ancestors for a node that matches the selector
        while(!selectedNode.matches(selector) && selectedNode !== domNode) {
            selectedNode = selectedNode.parentNode;
        }
        // If we found one, copy the attributes as variables, otherwise exit
        if(selectedNode.matches(selector)) {
            $tw.utils.each(selectedNode.attributes,function(attribute) {
                variables["dom-" + attribute.name] = attribute.value;
            });
        } else {
            return false;
        }
    }
    // Execute our actions with the variables
    if(actions) {
        self.invokeActionString(actions,self,event,variables);
        event.preventDefault();
        event.stopPropagation();
        return true;
    }
    return false;
}

@saqimtiaz suggested <$trap> for the name, which I think prefer too.

Most helpful comment

Shouldn't it be <$event-trap ... or something similar.

Just to be sure. This widget may be nested!

Consistency with the <$linkcatcher> widget might suggest <$eventcatcher>.

All 13 comments

@Jermolene I would be very happy to have something like this in the core. It basically gives us event delegation in TW. For application building it is incredibly useful. I am kicking myself for not thinking of this approach :)

I have similar performance problems in my Streams plugin, where I have a plethora of button widgets around single line tiddlers, as well as subclassed button widgets to capture dblclick and contextmenu events.

I can try to make some time this weekend to see where this needs refinement, unless you plan to tackle it yourself.

@Jermolene I would be very happy to have something like this in the core. It basically gives us event delegation in TW. For application building it is incredibly useful. I am kicking myself for not thinking of this approach :)

I have similar performance problems in my Streams plugin, where I have a plethora of button widgets around single line tiddlers, as well as subclassed button widgets to capture dblclick and contextmenu events.

I can try to make some time this weekend to see where this needs refinement, unless you plan to tackle it yourself.

Great thanks @saqimtiaz. Getting this into shape for the core has been on my todo list for a while so it would be great if you could pick it up.

@Jermolene will do.

So we are on the same page, here is my TW to-do list for the weekend:

  • work on system tiddler sync
  • extend <$action-log> widget with some more options and also expose it as <$log>
  • work on <$trap>
  • resolve broken offline snapshot for tiddlyweb saver

Thanks @saqimtiaz that's great. Perhaps at the start of next week we should come up with a timetable for v5.1.23. This is a good time to review any outstanding blockers for the release.

@Jermolene that sounds prudent. It would be a good time to announce a deadline for new PRs as well.

  • work on <$trap>

Shouldn't it be <$event-trap ... or something similar.

Just to be sure. This widget may be nested!

Since this pattern comes up very often now, IMO it should be part of the "base" widget.js.

        // If we found one, copy the attributes as variables, otherwise exit
        if(selectedNode.matches(selector)) {
            $tw.utils.each(selectedNode.attributes,function(attribute) {                 <- pattern
                variables["dom-" + attribute.name] = attribute.value;
            });
        } else {
            return false;
        }

also see: https://github.com/Jermolene/TiddlyWiki5/pull/5052#issuecomment-729220523 and link to source

I can create a PR if you want.

Since this pattern comes up very often now, IMO it should be part of the "base" widget.js.

      // If we found one, copy the attributes as variables, otherwise exit

      if(selectedNode.matches(selector)) {

          $tw.utils.each(selectedNode.attributes,function(attribute) {                 <- pattern

              variables["dom-" + attribute.name] = attribute.value;

          });

      } else {

          return false;

      }

also see: https://github.com/Jermolene/TiddlyWiki5/pull/5052#issuecomment-729220523 and link to source

I can create a PR if you want.

I think #5052 concerns copying widget attributes, while here we're copying DOM node attributes.

The function may be called like this:

Widget.prototype.prepareAttributes = function(prefix, attributes, options) {
.... deal with the prefix. 

variables = $tw.utils.extend(variables, options);  // options may be optional-vars ... or so.
return variables;
}

I think #5052 concerns copying widget attributes, while here we're copying DOM node attributes.

... uups. :)

Shouldn't it be <$event-trap ... or something similar.

Just to be sure. This widget may be nested!

Consistency with the <$linkcatcher> widget might suggest <$eventcatcher>.

@Jermolene good point, eventcatcher has my vote.

Not a fan of the proposed "trap" name, because I think non-programmers would look at it and go, "So, what does that do? The name tells me nothing." I like "eventcatcher" much better.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

saqimtiaz picture saqimtiaz  路  5Comments

saqimtiaz picture saqimtiaz  路  5Comments

diego898 picture diego898  路  5Comments

joshuafontany picture joshuafontany  路  5Comments

KendrickLamarck picture KendrickLamarck  路  4Comments