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:
.myclass will match events originating from a DOM node with the class "myclass")dom-event-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.
@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:
<$action-log> widget with some more options and also expose it as <$log><$trap>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.
Most helpful comment
Consistency with the
<$linkcatcher>widget might suggest<$eventcatcher>.