DOJO:onClick 并不总是在自定义小部件中调用
Posted
技术标签:
【中文标题】DOJO:onClick 并不总是在自定义小部件中调用【英文标题】:DOJO: onClick not always called in custom widget 【发布时间】:2015-07-28 13:15:22 【问题描述】:我遇到了 Dojo 1.10 的问题,需要一些关于如何找出罪魁祸首的建议。我有一个自定义小部件 TaskButton,它实现了 onMouseDown、onMouseUp 和 onClick 方法。这三个都有日志记录语句。 onMouseDown 和 onMouseUp 总是被调用,并且正确的时间和它们的日志语句显示在控制台中。但是,尽管反复单击 TaskButton,onClick 有时也不会被调用。大多数情况下,单击 TaskButton 外部然后返回内部会使 onClick 工作,但并非总是如此。当 onClick 没有被调用时,它的日志语句不会显示在控制台中。
TaskButton.js 自定义小部件
define([
"dojo/_base/declare",
"dojo/_base/event",
"dojo/_base/lang",
"dojo/dom-class",
"dojo/dom-construct",
"dojo/mouse",
"dojo/on",
"dojo/query",
"dojo/topic",
"dijit/Menu",
"dijit/MenuItem",
"dijit/MenuSeparator",
"dijit/PopupMenuItem",
"dijit/popup",
"dijit/Tooltip",
"dijit/Tree",
"dijit/tree/ForestStoreModel",
"dijit/registry",
"dijit/form/Button",
"dijit/_WidgetBase",
"dijit/_OnDijitClickMixin",
"dijit/_TemplatedMixin",
"dijit/_WidgetsInTemplateMixin",
"dojo/text!./templates/TaskButton.html"
], function(declare, event, lang, domClass, domConstruct, mouse, on, query, topic, Menu, MenuItem, MenuSeparator, PopupMenuItem,
Popup, Tooltip, Tree, ForestStoreModel, registry, button, _WidgetBase, _OnDijitClickMixin, _TemplatedMixin, _WidgetsInTemplateMixin, template)
return declare("TaskButton", [_WidgetBase, _OnDijitClickMixin, _TemplatedMixin, _WidgetsInTemplateMixin, Menu],
scene:0,
sceneId:0,
target:"",
state:"pending",
cloudCover: false,
cloudPercentage: 0,
targetInterest: false,
hsv: false,
previousState:"pending",
backgroundcolor:"#414141",
templateString:template,
baseClass: "TaskButton",
innerNode:undefined,
cm:null,
theTask:null,
eventHandle:null,
postCreate: function()
// Get a DOM node reference for the root of our widget
var domNode = this.domNode;
this.innerNode = domNode.firstChild.nextElementSibling.firstElementChild;
domClass.replace(this.innerFill, "task"+this.state+"Background", "task"+this.state+"Background");
if (this.cloudCover && ((this.state === "Ready") || (this.state === "Unassigned")))
domClass.replace(this.innerFill, "task"+"Red"+"Background", "task"+this.state+"Background");
this.previousState = this.state;
console.log("getting context menu for Scene-" + this.scene + "ContextMenu");
cm = registry.byId("Scene-" + this.scene + "ContextMenu");
this.own(
on(domNode, "contextmenu", lang.hitch(this, "_showContextMenu"))
);
this.inherited(arguments);
,
startup: function()
//Turn off button icons if warranted Must do here after dom nodes built
if (!this.cloudCover)
dojo.style(dojo.byId("Scene-"+this.scene+"Cloud"), "display", "none");
if (!this.targetInterest)
dojo.style(dojo.byId("Scene-"+this.scene+"Target"), "display", "none");
if (!this.hsv)
dojo.style(dojo.byId("Scene-"+this.scene+"HSV"), "display", "none");
this.inherited(arguments);
,
test: function(sceneId)
console.log("testing");
if (sceneId != this.scene)
domClass.replace("Scene-" + sceneId + "Fill", "taskInnerFill", "taskInnerFillSelected");
,
buildRendering: function()
console.log("buildRendering scene:" + this.scene);
this.inherited(arguments);
,
//
uninitialize: function()
if (this.eventHandle != null)
console.log("unsubscribing from event topic");
eventHandle.remove();
eventHandle = null;
this.inherited(arguments);
,
//
_onMenuClick: function(event)
console.log("menu item clicked");
,
_showContextMenu: function(event)
console.log("opening context menu for scene:" + this.scene);
this.inherited(arguments);
,
// This is always called
_onMouseDown: function(e)
var scene = e.currentTarget.attributes["scene"].value;
if (e.button == 0)
console.log("mouse left pressed, scene=" + scene + " button=" + e.button);
domClass.replace("Scene-" + scene + "OuterBorder", "taskOuterBorderPressed", "taskOuterBorder");
else if (e.button == 2)
console.log("mouse right pressed, scene=" + scene + " button=" + e.button);
domClass.replace("Scene-" + scene + "OuterBorder", "taskOuterBorderPressed", "taskOuterBorder");
this.inherited(arguments);
,
// This is always called
_onMouseUp: function(e)
var scene = e.currentTarget.attributes["scene"].value;
if (e.button == 0)
console.log("mouse left released, scene=" + scene + " button=" + e.button);
else if (e.button == 2)
console.log("mouse right released, scene=" + scene + " button=" + e.button);
domClass.replace("Scene-" + this.scene + "OuterBorder", "taskOuterBorder", "taskOuterBorderPressed");
dijit.hideTooltip(e.currentTarget);
this.inherited(arguments);
,
//
_onMouseEnter: function(e)
label = "Scene: " + this.scene + "<BR>State: " + this.state + "<BR>Target: " + this.target;
dijit.showTooltip(label,e.currentTarget);
dijit.popup.close();
this.inherited(arguments);
,
//
_onMouseLeave: function(e)
this._onMouseUp("");
this.inherited(arguments);
dijit.hideTooltip(e.currentTarget);
,
// This is what is not always called
_onClick: function(e)
var scene = e.currentTarget.attributes["scene"].value;
console.log("scene " + scene + " clicked");
this._publishEvent(this.scene, "clicked");
this.inherited(arguments);
,
//
_onBlur: function(e)
dijit.popup.close();
this.inherited(arguments);
,
//
_onContextMenu: function(e)
this.inherited(arguments);
this._publishEvent("scene":this.scene,"sceneId":this.sceneId, "clicked");
dijit.hideTooltip(e.currentTarget);
var widget = this;
theNode = "TaskButtonContainer" + widget.scene;
console.log("mouse right clicked, scene=" + widget.scene + " target: " + e.target + "current target");
theTask = missionCache.query("sceneId" : this.scene).then( function(results)
theTask = results;
if (widget.state === "Unassigned" || widget.state === "Ready")
//The context menu should fire to allow assignment
var cb = new dijit.form.ComboBox(style:"width:96%;background-color:#414141;margin-top:4px;margin-bottom:4px;",
name:"usersByTask", placeholder:"Assign this task to: ", store:usersContextMenuCB,
labelAttr: 'name',
searchAttr: 'name',
onChange: function()
theTask[0].taskStatus = "Assigned";
theTask[0].taskOwner = this.item.userName;
missionCache.put(theTask[0]);
console.log("nothing");
widget.set("state", "Assigned");
assignTask(this.item.userName);
widget.domNode.classList.remove("Unassigned");
widget.domNode.classList.add("Assigned");
widget.domNode.setAttribute("dndtype", "Assigned");
this.destroy();
,
onClose: function() this.destroy();
);
cb.toggleDropDown();
dijit.popup.open(parent: widget, popup:cb, around:e.target,
onClose: function()
dijit.popup.close(cb);
);
);
,
_publishEvent: function(sceneNumber, eventName)
console.log("publishing " + eventName + " for scene " + sceneNumber);
topic.publish("TaskButton/tasks", scene:sceneNumber, task:this, event:eventName );
,
_setStateAttr: function(newState)
if (newState != "")
console.log("setting state for scene:" + this.scene + " to " + newState);
this._set("state", newState);
if (this.innerNode !== undefined)
domClass.replace(this.innerFill, "task"+newState+"Background", "task"+this.previousState+"Background");
this.previousState = this.state;
this.state = newState;
this.inherited(arguments);
,
_changeTaskState: function(newState)
require(["dijit/registry"], function(registry)
var node = registry.byId(clickedItem);
if (node !== undefined)
node.set("state", newState);
console.log("changed task " + this.clickedItem + " state to " + newState);
);
this.inherited(arguments);
,
_menuTaskDetails: function(e)
console.log("do task deatils");
);
)
模板中的所有按钮动作都有附加事件。
TaskButton.html 模板:
<li class="dojoDndItem" dndType="$state" style="border:none;padding:0" data-dojo-props="scene:$scene">
<div id="TaskButtonContainer-$scene" widgetid="TaskButtonContainer-$scene" class="$baseClass" data-dojo-attach-point="taskButtonContainer"
data-dojo-attach-event="onContextMenu:_showContextMenu">
<div widgetid="Scene-$sceneContextMenu" data-dojo-type="dijit/Menu" data-dojo-props="contextMenuForWindow:false"
data-dojo-attach-point="contextMenu" targetNodeIds="Scene-$sceneFill" style="display: none;">
<div data-dojo-type="dijit/MenuItem" data-dojo-attach-event="onClick:_menuTaskDetails">
Task Details
</div>
</div>
<div id="Scene-$sceneOuterBorder" widgetid="Scene-$sceneOuterBorder" class="taskOuterBorder" data-dojo-attach-point="outerBorder" scene="$scene">
<div id="Scene-$sceneFill" class="taskInnerFill task$stateBackground" data-dojo-attach-point="innerFill" scene="$scene"
data-dojo-attach-event="onMouseDown:_onMouseDown,onMouseUp:_onMouseUp,onDijitClick:_onClick,onMouseEnter:_onMouseEnter,onMouseLeave:_onMouseLeave,onContextMenu:_onContextMenu,onBlur:_onBlur">
<div id="Scene-$sceneText" class="taskText" data-dojo-attach-point="text">
<table style="margin:0;padding:0">
<tr>
<td>$scene</td>
</tr>
<tr>
<td>
<img id="Scene-$sceneCloud" src="img/cloud.png" >
<img id="Scene-$sceneTarget" src="img/target.png" >
<img id="Scene-$sceneHSV" src="img/HSV.png" >
</td>
</tr>
</table>
</div>
</div>
</div>
</div>
</li>
我还根据建议清理了代码,TaskButton 的 onClick 事件处理程序的行为没有变化。
【问题讨论】:
可以分享一下TaskButton的代码吗?没有它就很难猜出哪里出了问题。 【参考方案1】:嗯, 有多个错误...
你不需要继承_WidgetBase
、_TemplatedMixin
和_OnDijitClickMixin
,它们带有Menu
Menu
应该是 Base(所以应该是继承列表中的第一个)
您不应该使用domNode.firstChild.nextElementSibling.firstElementChild
,而是应该在模板中使用data-dojo-attach-point
cm = registry.byId('Scene-' + this.scene + 'ContextMenu');
应该是 this.cm = registry.byId('Scene-' + this.scene + 'ContextMenu');
不应使用 dojo
命名空间。因此,dojo.style
应替换为 dojo/dom-style
和 domStyle.set()
的要求,dojo.byId
应替换为 dojo/dom
和 dom.byId()
的要求
不应使用 dijit
命名空间。因此,dijit.hideTooltip
应替换为 dijit/Tooltip
的要求,然后 Tooltip.hide()
和 dijit.showTooltip
应替换为 Tooltip.show()
和 dijit.popup.close()
应替换为 dijit/_base/popup
的要求,然后 popup.close()
和 @987654346 @ 应替换为 popup.open()
在方法_onMouseEnter
中label
之前缺少var
在dijit.form.ComboBox
的onChange
中,您调用了一个不存在的方法assignTask
在_changeTaskState
方法中,您需要dijit/registry
,但它已经可用。所以额外的要求是没有用的
可能还有更多,但我不会去重构你的代码
终于没有任何东西附加到您的_onClick
方法上。我不明白它有时是如何执行的......但也许它是使用data-dojo-attach-event
连接的?如果是,请同时提供您的模板。
尝试应用所有更改来清除一些错误,看看是否效果更好。 如果没有,请分享您的按钮模板。
define([
'dojo/_base/declare',
'dojo/_base/event',
'dojo/_base/lang',
'dojo/dom',
'dojo/dom-class',
'dojo/dom-construct',
'dojo/dom-style',
'dojo/mouse',
'dojo/on',
'dojo/query',
'dojo/topic',
'dijit/Menu',
'dijit/MenuItem',
'dijit/MenuSeparator',
'dijit/PopupMenuItem',
'dijit/popup',
'dijit/Tooltip',
'dijit/Tree',
'dijit/tree/ForestStoreModel',
'dijit/registry',
'dijit/_base/popup',
'dijit/form/Button',
'dijit/_WidgetBase',
'dijit/_OnDijitClickMixin',
'dijit/_TemplatedMixin',
'dijit/_WidgetsInTemplateMixin',
'dojo/text!./templates/TaskButton.html'
], function(declare, event, lang, dom, domClass, domConstruct, domStyle, mouse, on, query, topic, Menu, MenuItem, MenuSeparator, PopupMenuItem,
Popup, Tooltip, Tree, ForestStoreModel, registry, popup, button, _WidgetBase, _OnDijitClickMixin, _TemplatedMixin, _WidgetsInTemplateMixin, template)
return declare('TaskButton', [Menu, _WidgetsInTemplateMixin],
scene: 0,
sceneId: 0,
target: '',
state: 'pending',
cloudCover: false,
cloudPercentage: 0,
targetInterest: false,
hsv: false,
previousState: 'pending',
backgroundcolor: '#414141',
templateString: template,
baseClass: 'TaskButton',
innerNode: undefined,
cm: null,
theTask: null,
eventHandle: null,
postCreate: function()
// Get a DOM node reference for the root of our widget
var domNode = this.domNode;
this.innerNode = domNode.firstChild.nextElementSibling.firstElementChild;
domClass.replace(this.innerFill, 'task' + this.state + 'Background', 'task' + this.state + 'Background');
if (this.cloudCover && ((this.state === 'Ready') || (this.state === 'Unassigned')))
domClass.replace(this.innerFill, 'task' + 'Red' + 'Background', 'task' + this.state + 'Background');
this.previousState = this.state;
console.log('getting context menu for Scene-' + this.scene + 'ContextMenu');
cm = registry.byId('Scene-' + this.scene + 'ContextMenu');
this.own(
on(domNode, 'contextmenu', lang.hitch(this, '_showContextMenu'))
);
this.inherited(arguments);
,
startup: function()
//Turn off button icons if warranted Must do here after dom nodes built
if (!this.cloudCover)
domStyle.set(dom.byId('Scene-' + this.scene + 'Cloud'), 'display', 'none');
if (!this.targetInterest)
domStyle.set(dom.byId('Scene-' + this.scene + 'Target'), 'display', 'none');
if (!this.hsv)
domStyle.set(dom.byId('Scene-' + this.scene + 'HSV'), 'display', 'none');
this.inherited(arguments);
,
test: function(sceneId)
console.log('testing');
if (sceneId != this.scene)
domClass.replace('Scene-' + sceneId + 'Fill', 'taskInnerFill', 'taskInnerFillSelected');
,
buildRendering: function()
console.log('buildRendering scene:' + this.scene);
this.inherited(arguments);
,
//
uninitialize: function()
if (this.eventHandle != null)
console.log('unsubscribing from event topic');
eventHandle.remove();
eventHandle = null;
this.inherited(arguments);
,
//
_onMenuClick: function(event)
console.log('menu item clicked');
,
_showContextMenu: function(event)
console.log('opening context menu for scene:' + this.scene);
this.inherited(arguments);
,
// This is always called
_onMouseDown: function(e)
var scene = e.currentTarget.attributes['scene'].value;
if (e.button == 0)
console.log('mouse left pressed, scene=' + scene + ' button=' + e.button);
domClass.replace('Scene-' + scene + 'OuterBorder', 'taskOuterBorderPressed', 'taskOuterBorder');
else if (e.button == 2)
console.log('mouse right pressed, scene=' + scene + ' button=' + e.button);
domClass.replace('Scene-' + scene + 'OuterBorder', 'taskOuterBorderPressed', 'taskOuterBorder');
this.inherited(arguments);
,
// This is always called
_onMouseUp: function(e)
var scene = e.currentTarget.attributes['scene'].value;
if (e.button == 0)
console.log('mouse left released, scene=' + scene + ' button=' + e.button);
else if(e.button == 2)
console.log('mouse right released, scene=' + scene + ' button=' + e.button);
domClass.replace('Scene-' + this.scene + 'OuterBorder', 'taskOuterBorder', 'taskOuterBorderPressed');
Tooltip.hide(e.currentTarget);
this.inherited(arguments);
,
//
_onMouseEnter: function(e)
var label = 'Scene: ' + this.scene + '<BR>State: ' + this.state + '<BR>Target: ' + this.target;
Tooltip.show(label, e.currentTarget);
popup.close();
this.inherited(arguments);
,
//
_onMouseLeave: function(e)
this._onMouseUp('');
this.inherited(arguments);
Tooltip.hide(e.currentTarget);
,
// This is what is not always called
_onClick: function(e)
var scene = e.currentTarget.attributes['scene'].value;
console.log('scene ' + scene + ' clicked');
this._publishEvent(this.scene, 'clicked');
this.inherited(arguments);
,
//
_onBlur: function(e)
popup.close();
this.inherited(arguments);
,
//
_onContextMenu: function(e)
this.inherited(arguments);
this._publishEvent(
'scene': this.scene,
'sceneId': this.sceneId
, 'clicked');
Tooltip.hide(e.currentTarget);
var widget = this;
theNode = 'TaskButtonContainer' + widget.scene;
console.log('mouse right clicked, scene=' + widget.scene + ' target: ' + e.target + 'current target');
theTask = missionCache.query(
'sceneId': this.scene
).then(function(results)
theTask = results;
if (widget.state === 'Unassigned' || widget.state === 'Ready')
//The context menu should fire to allow assignment
var cb = new dijit.form.ComboBox(
style: 'width:96%;background-color:#414141;margin-top:4px;margin-bottom:4px;',
name: 'usersByTask',
placeholder: 'Assign this task to: ',
store: usersContextMenuCB,
labelAttr: 'name',
searchAttr: 'name',
onChange: function()
theTask[0].taskStatus = 'Assigned';
theTask[0].taskOwner = this.item.userName;
missionCache.put(theTask[0]);
console.log('nothing');
widget.set('state', 'Assigned');
//assignTask(this.item.userName);
widget.domNode.classList.remove('Unassigned');
widget.domNode.classList.add('Assigned');
widget.domNode.setAttribute('dndtype', 'Assigned');
this.destroy();
,
onClose: function()
this.destroy();
);
cb.toggleDropDown();
popup.open(
parent: widget,
popup: cb,
around: e.target,
onClose: function()
popup.close(cb);
);
);
,
_publishEvent: function(sceneNumber, eventName)
console.log('publishing ' + eventName + ' for scene ' + sceneNumber);
topic.publish('TaskButton/tasks',
scene: sceneNumber,
task: this,
event: eventName
);
,
_setStateAttr: function(newState)
if (newState != '')
console.log('setting state for scene:' + this.scene + ' to ' + newState);
this._set('state', newState);
if (this.innerNode !== undefined)
domClass.replace(this.innerFill, 'task' + newState + 'Background', 'task' + this.previousState + 'Background');
this.previousState = this.state;
this.state = newState;
this.inherited(arguments);
,
_changeTaskState: function(newState)
var node = registry.byId(clickedItem);
if (node !== undefined)
node.set('state', newState);
console.log('changed task ' + this.clickedItem + ' state to ' + newState);
this.inherited(arguments);
,
_menuTaskDetails: function(e)
console.log('do task deatils');
);
)
【讨论】:
以上是关于DOJO:onClick 并不总是在自定义小部件中调用的主要内容,如果未能解决你的问题,请参考以下文章