1
0
mirror of https://github.com/Jermolene/TiddlyWiki5 synced 2024-11-23 10:07:19 +00:00

Extend Eventcatcher to handle multiple events (#5185)

* support multiple events

* Add variables for event type and detail. Rename types attribute to events

* Correct typo in refresh handling
This commit is contained in:
Saq Imtiaz 2020-12-02 22:15:35 +01:00 committed by GitHub
parent 6b03105bed
commit 1f5e1205ec
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 83 additions and 67 deletions

View File

@ -43,66 +43,76 @@ EventWidget.prototype.render = function(parent,nextSibling) {
// Assign classes // Assign classes
this.assignDomNodeClasses(); this.assignDomNodeClasses();
// Add our event handler // Add our event handler
domNode.addEventListener(this.type,function(event) { $tw.utils.each(this.types,function(type) {
var selector = self.getAttribute("selector"), domNode.addEventListener(type,function(event) {
actions = self.getAttribute("actions"), var selector = self.getAttribute("selector"),
selectedNode = event.target, actions = self.getAttribute("actions-"+type),
selectedNodeRect, selectedNode = event.target,
catcherNodeRect, selectedNodeRect,
variables = {}; catcherNodeRect,
if(selector) { variables = {};
// Search ancestors for a node that matches the selector if(selector) {
while(!selectedNode.matches(selector) && selectedNode !== domNode) { // Search ancestors for a node that matches the selector
selectedNode = selectedNode.parentNode; while(!selectedNode.matches(selector) && selectedNode !== domNode) {
} selectedNode = selectedNode.parentNode;
// If we found one, copy the attributes as variables, otherwise exit }
if(selectedNode.matches(selector)) { // If we found one, copy the attributes as variables, otherwise exit
$tw.utils.each(selectedNode.attributes,function(attribute) { if(selectedNode.matches(selector)) {
variables["dom-" + attribute.name] = attribute.value.toString(); $tw.utils.each(selectedNode.attributes,function(attribute) {
}); variables["dom-" + attribute.name] = attribute.value.toString();
//Add a variable with a popup coordinate string for the selected node });
variables["tv-popup-coords"] = "(" + selectedNode.offsetLeft + "," + selectedNode.offsetTop +"," + selectedNode.offsetWidth + "," + selectedNode.offsetHeight + ")"; //Add a variable with a popup coordinate string for the selected node
variables["tv-popup-coords"] = "(" + selectedNode.offsetLeft + "," + selectedNode.offsetTop +"," + selectedNode.offsetWidth + "," + selectedNode.offsetHeight + ")";
//Add variables for offset of selected node
variables["tv-selectednode-posx"] = selectedNode.offsetLeft.toString(); //Add variables for offset of selected node
variables["tv-selectednode-posy"] = selectedNode.offsetTop.toString(); variables["tv-selectednode-posx"] = selectedNode.offsetLeft.toString();
variables["tv-selectednode-width"] = selectedNode.offsetWidth.toString(); variables["tv-selectednode-posy"] = selectedNode.offsetTop.toString();
variables["tv-selectednode-height"] = selectedNode.offsetHeight.toString(); variables["tv-selectednode-width"] = selectedNode.offsetWidth.toString();
variables["tv-selectednode-height"] = selectedNode.offsetHeight.toString();
//Add variables for event X and Y position relative to selected node //Add variables for event X and Y position relative to selected node
selectedNodeRect = selectedNode.getBoundingClientRect(); selectedNodeRect = selectedNode.getBoundingClientRect();
variables["event-fromselected-posx"] = (event.clientX - selectedNodeRect.left).toString(); variables["event-fromselected-posx"] = (event.clientX - selectedNodeRect.left).toString();
variables["event-fromselected-posy"] = (event.clientY - selectedNodeRect.top).toString(); variables["event-fromselected-posy"] = (event.clientY - selectedNodeRect.top).toString();
//Add variables for event X and Y position relative to event catcher node //Add variables for event X and Y position relative to event catcher node
catcherNodeRect = self.domNode.getBoundingClientRect(); catcherNodeRect = self.domNode.getBoundingClientRect();
variables["event-fromcatcher-posx"] = (event.clientX - catcherNodeRect.left).toString(); variables["event-fromcatcher-posx"] = (event.clientX - catcherNodeRect.left).toString();
variables["event-fromcatcher-posy"] = (event.clientY - catcherNodeRect.top).toString(); variables["event-fromcatcher-posy"] = (event.clientY - catcherNodeRect.top).toString();
} else { } else {
return false; return false;
}
}
// Execute our actions with the variables
if(actions) {
// Add a variable for the modifier key
variables.modifier = $tw.keyboardManager.getEventModifierKeyDescriptor(event);
// Add a variable for the mouse button
if("button" in event) {
if(event.button === 0) {
variables["event-mousebutton"] = "left";
} else if(event.button === 1) {
variables["event-mousebutton"] = "middle";
} else if(event.button === 2) {
variables["event-mousebutton"] = "right";
} }
} }
self.invokeActionString(actions,self,event,variables); // Execute our actions with the variables
event.preventDefault(); if(actions) {
event.stopPropagation(); // Add a variable for the modifier key
return true; variables.modifier = $tw.keyboardManager.getEventModifierKeyDescriptor(event);
} // Add a variable for the mouse button
return false; if("button" in event) {
},false); if(event.button === 0) {
variables["event-mousebutton"] = "left";
} else if(event.button === 1) {
variables["event-mousebutton"] = "middle";
} else if(event.button === 2) {
variables["event-mousebutton"] = "right";
}
}
variables["event-type"] = event.type;
if(typeof event.detail === "object" && !!event.detail) {
$tw.utils.each(event.detail,function(detailValue,detail) {
variables["event-detail-" + detail] = detailValue;
});
} else if(!!event.detail) {
variables["event-detail"] = event.detail;
}
self.invokeActionString(actions,self,event,variables);
event.preventDefault();
event.stopPropagation();
return true;
}
return false;
},false);
});
// Insert element // Insert element
parent.insertBefore(domNode,nextSibling); parent.insertBefore(domNode,nextSibling);
this.renderChildren(domNode,null); this.renderChildren(domNode,null);
@ -115,7 +125,7 @@ Compute the internal state of the widget
EventWidget.prototype.execute = function() { EventWidget.prototype.execute = function() {
var self = this; var self = this;
// Get attributes that require a refresh on change // Get attributes that require a refresh on change
this.type = this.getAttribute("type"); this.types = this.getAttribute("events","").split(" ");
this.elementTag = this.getAttribute("tag"); this.elementTag = this.getAttribute("tag");
// Make child widgets // Make child widgets
this.makeChildWidgets(); this.makeChildWidgets();
@ -132,7 +142,7 @@ Selectively refreshes the widget if needed. Returns true if the widget or any of
*/ */
EventWidget.prototype.refresh = function(changedTiddlers) { EventWidget.prototype.refresh = function(changedTiddlers) {
var changedAttributes = this.computeAttributes(); var changedAttributes = this.computeAttributes();
if(changedAttributes.type || changedAttributes["tag"]) { if(changedAttributes["events"] || changedAttributes["tag"]) {
this.refreshSelf(); this.refreshSelf();
return true; return true;
} else if(changedAttributes["class"]) { } else if(changedAttributes["class"]) {

View File

@ -1,5 +1,5 @@
created: 20201123113532200 created: 20201123113532200
modified: 20201123172753436 modified: 20201202200719126
tags: Widgets tags: Widgets
title: EventCatcherWidget title: EventCatcherWidget
type: text/vnd.tiddlywiki type: text/vnd.tiddlywiki
@ -11,10 +11,10 @@ The event catcher widget traps JavaScript events dispatched within its child con
In order for the events to be trapped they must: In order for the events to be trapped they must:
* be of the type specified as a parameter to the event catcher widget. * be of one of the types specified as a parameter to the event catcher widget.
* arise within a DOM node matching the selector specified as a parameter to the widget. * arise within a DOM node matching the selector specified as a parameter to the widget.
Use of the event catcher widget is useful when using large numbers of other trigger widgets such as the ButtonWidget is causing performance problems. The workflow it enables is akin to what is referred to as "event delegation" in JavaScript parlance. Use of the event catcher widget is beneficial when using large numbers of other trigger widgets such as the ButtonWidget is causing performance problems. The workflow it enables is akin to what is referred to as "event delegation" in JavaScript parlance.
//This is an advanced widget intended to be used by those familiar with HTML, CSS and JavaScript.// //This is an advanced widget intended to be used by those familiar with HTML, CSS and JavaScript.//
@ -23,9 +23,9 @@ Use of the event catcher widget is useful when using large numbers of other trig
The content of the `<$eventcatcher>` widget is displayed normally. The content of the `<$eventcatcher>` widget is displayed normally.
|!Attribute |!Description | |!Attribute |!Description |
|type |The JavaScript event type to be trapped, for example "click", or "dblclick" | |events |Space separated list of JavaScript events to be trapped, for example "click" or "click dblclick" |
|selector |A CSS selector. Only events originating inside a DOM node with this selector will be trapped. | |selector |A CSS selector. Only events originating inside a DOM node with this selector will be trapped. |
|actions |Action strings to be invoked when a matching event is trapped | |actions-* |Action strings to be invoked when a matching event is trapped. Each event is mapped to an action attribute name of the form `actions-"event"` where `event` represents the type of the event. For example: `actions-click` or `actions-dblclick` |
|class |An optional CSS class name to be assigned to the HTML element | |class |An optional CSS class name to be assigned to the HTML element |
|tag |Optional. The html element the widget creates to capture the events, defaults to:<br>» `span` when parsed in inline-mode<br>» `div` when parsed in block-mode | |tag |Optional. The html element the widget creates to capture the events, defaults to:<br>» `span` when parsed in inline-mode<br>» `div` when parsed in block-mode |
@ -37,6 +37,8 @@ The following variables are made available to the actions:
|`dom-*` |All DOM attributes of the node matching the given selector are made available as variables, with the prefix `dom-` | |`dom-*` |All DOM attributes of the node matching the given selector are made available as variables, with the prefix `dom-` |
|`modifier` |The [[modifier Variable]] contains the Modifier Key held during the event (can be "normal", "ctrl", "shift", "alt" or combinations thereof) | |`modifier` |The [[modifier Variable]] contains the Modifier Key held during the event (can be "normal", "ctrl", "shift", "alt" or combinations thereof) |
|`event-mousebutton`|The mouse button (if any) used to trigger the event (can be "left", "right" or "middle"). Note that not all event types support the mousebutton property | |`event-mousebutton`|The mouse button (if any) used to trigger the event (can be "left", "right" or "middle"). Note that not all event types support the mousebutton property |
|`event-type`|The type property of the JavaScript event |
|`event-detail-*`|Any properties in the detail attribute of the event are made available with the prefix `event-detail-` |
|`tv-popup-coords`|A co-ordinate string that can be used with the ActionPopupWidget to trigger a popup at the DOM node matching the selector where the event originated | |`tv-popup-coords`|A co-ordinate string that can be used with the ActionPopupWidget to trigger a popup at the DOM node matching the selector where the event originated |
|`tv-selectednode-posx`|`x` offset position of the selected DOM node | |`tv-selectednode-posx`|`x` offset position of the selected DOM node |
|`tv-selectednode-posy`|`y` offset position of the selected DOM node | |`tv-selectednode-posy`|`y` offset position of the selected DOM node |
@ -52,11 +54,15 @@ The following variables are made available to the actions:
This example uses the ActionLogWidget and will log the `data-item-id` attribute of the clicked DOM node to the browser's JavaScript [[console|Web Developer Tools]] This example uses the ActionLogWidget and will log the `data-item-id` attribute of the clicked DOM node to the browser's JavaScript [[console|Web Developer Tools]]
``` ```
\define myactions() \define clickactions()
<$action-log item=<<dom-data-item-id>>/> <$action-log item=<<dom-data-item-id>> event=<<event-type>>/>
\end \end
<$eventcatcher type="click" selector=".item" actions=<<myactions>> tag="div"> \define contextmenu-actions()
<$action-log item=<<dom-data-item-id>> event=<<event-type>>/>
\end
<$eventcatcher events="click contextmenu" selector=".item" actions-click=<<clickactions>> actions-contextmenu=<<contextmenu-actions>> tag="div">
<div class="item" data-item-id="item1"> <div class="item" data-item-id="item1">
Click events here will be trapped Click events here will be trapped
@ -74,6 +80,6 @@ Not here
And here And here
</div> </div>
</$eventcatcher>""" </$eventcatcher>
``` ```