【问题标题】:DOJO: onClick not always called in custom widgetDOJO:onClick 并不总是在自定义小部件中调用
【发布时间】: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-${scene}ContextMenu" data-dojo-type="dijit/Menu" data-dojo-props="contextMenuForWindow:false"
       data-dojo-attach-point="contextMenu" targetNodeIds="Scene-${scene}Fill" style="display: none;">
    <div data-dojo-type="dijit/MenuItem" data-dojo-attach-event="onClick:_menuTaskDetails">
      Task Details
    </div>
  </div>
  <div id="Scene-${scene}OuterBorder" widgetid="Scene-${scene}OuterBorder" class="taskOuterBorder" data-dojo-attach-point="outerBorder" scene="${scene}">
    <div id="Scene-${scene}Fill" class="taskInnerFill task${state}Background" 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-${scene}Text" class="taskText" data-dojo-attach-point="text">
            <table style="margin:0;padding:0">
              <tr>
                <td>${scene}</td>
              </tr>
              <tr>
                <td>
                  <img id="Scene-${scene}Cloud" src="img/cloud.png" alt="Cloud cover" height="21" width="21">
                  <img id="Scene-${scene}Target" src="img/target.png" alt="ATR" height="21" width="21">
                  <img id="Scene-${scene}HSV" src="img/HSV.png" alt="HSV" height="21" width="21">
                </td>
              </tr>
            </table>
         </div>
      </div>
    </div>
    </div>
    </li>

我还根据建议清理了代码,TaskButton 的 onClick 事件处理程序的行为没有变化。

【问题讨论】:

  • 可以分享一下TaskButton的代码吗?没有它就很难猜出哪里出了问题。

标签: events dojo onclick widget


【解决方案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-styledomStyle.set() 的要求,dojo.byId 应替换为 dojo/domdom.byId() 的要求
  • 不应使用 dijit 命名空间。因此,dijit.hideTooltip 应替换为 dijit/Tooltip 的要求,然后 Tooltip.hide()dijit.showTooltip 应替换为 Tooltip.show()dijit.popup.close() 应替换为 dijit/_base/popup 的要求,然后 popup.close() 和 @987654346 @ 应替换为 popup.open()
  • 在方法_onMouseEnterlabel 之前缺少var
  • dijit.form.ComboBoxonChange 中,您调用了一个不存在的方法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');
        }

    });
})

【讨论】:

    猜你喜欢
    • 2013-05-29
    • 2011-08-12
    • 1970-01-01
    • 2017-07-06
    • 1970-01-01
    • 1970-01-01
    • 2016-07-26
    • 2017-01-15
    • 1970-01-01
    相关资源
    最近更新 更多