dijit/_CssStateMixin.js

  • Provides:

    • dijit._CssStateMixin
  • dijit._CssStateMixin

    • type
      Function
    • summary
      Mixin for widgets to set CSS classes on the widget DOM nodes depending on hover/mouse press/focus
      state changes, and also higher-level state changes such becoming disabled or selected.
    • description
      By mixing this class into your widget, and setting the this.baseClass attribute, it will automatically
      maintain CSS classes on the widget root node (this.domNode) depending on hover,
      active, focus, etc. state.   Ex: with a baseClass of dijitButton, it will apply the classes
      dijitButtonHovered and dijitButtonActive, as the user moves the mouse over the widget and clicks it.
      
      It also sets CSS like dijitButtonDisabled based on widget semantic state.
      
      By setting the cssStateNodes attribute, a widget can also track events on subnodes (like buttons
      within the widget).
  • dijit._CssStateMixin.cssStateNodes

    • type
      Object
    • summary
  • dijit._CssStateMixin.hovering

    • tags: readonly
    • type
      Boolean
    • summary
      True if cursor is over this widget
  • dijit._CssStateMixin.active

    • tags: readonly
    • type
      Boolean
    • summary
      True if mouse was pressed while over this widget, and hasn't been released yet
  • dijit._CssStateMixin._applyAttributes

    • type
      Function
    • source: [view]
      define("dijit/_CssStateMixin", ["dojo", "dijit"], function(dojo, dijit) {


      dojo.declare("dijit._CssStateMixin", [], {
       // summary:
       //  Mixin for widgets to set CSS classes on the widget DOM nodes depending on hover/mouse press/focus
       //  state changes, and also higher-level state changes such becoming disabled or selected.
       //
       // description:
       //  By mixing this class into your widget, and setting the this.baseClass attribute, it will automatically
       //  maintain CSS classes on the widget root node (this.domNode) depending on hover,
       //  active, focus, etc. state. Ex: with a baseClass of dijitButton, it will apply the classes
       //  dijitButtonHovered and dijitButtonActive, as the user moves the mouse over the widget and clicks it.
       //
       //  It also sets CSS like dijitButtonDisabled based on widget semantic state.
       //
       //  By setting the cssStateNodes attribute, a widget can also track events on subnodes (like buttons
       //  within the widget).


       // cssStateNodes: [protected] Object
       //  List of sub-nodes within the widget that need CSS classes applied on mouse hover/press and focus
       //.
       //  Each entry in the hash is a an attachpoint names (like "upArrowButton") mapped to a CSS class names
       //  (like "dijitUpArrowButton"). Example:
       // |  {
       // |   "upArrowButton": "dijitUpArrowButton",
       // |   "downArrowButton": "dijitDownArrowButton"
       // |  }
       //  The above will set the CSS class dijitUpArrowButton to the this.upArrowButton DOMNode when it
       //  is hovered, etc.
       cssStateNodes: {},


       // hovering: [readonly] Boolean
       //  True if cursor is over this widget
       hovering: false,

       
       // active: [readonly] Boolean
       //  True if mouse was pressed while over this widget, and hasn't been released yet
       active: false,


       _applyAttributes: function(){
        // This code would typically be in postCreate(), but putting in _applyAttributes() for
        // performance: so the class changes happen before DOM is inserted into the document.
        // Change back to postCreate() in 2.0. See #11635.


        this.inherited(arguments);


        // Automatically monitor mouse events (essentially :hover and :active) on this.domNode
        dojo.forEach(["onmouseenter", "onmouseleave", "onmousedown"], function(e){
         this.connect(this.domNode, e, "_cssMouseEvent");
        }, this);

        
        // Monitoring changes to disabled, readonly, etc. state, and update CSS class of root node
        dojo.forEach(["disabled", "readOnly", "checked", "selected", "focused", "state", "hovering", "active"], function(attr){
         this.watch(attr, dojo.hitch(this, "_setStateClass"));
        }, this);


        // Events on sub nodes within the widget
        for(var ap in this.cssStateNodes){
         this._trackMouseState(this[ap], this.cssStateNodes[ap]);
        }
        // Set state initially; there's probably no hover/active/focus state but widget might be
        // disabled/readonly/checked/selected so we want to set CSS classes for those conditions.
        this._setStateClass();
    • summary
  • dijit._CssStateMixin._cssMouseEvent

    • type
      Function
    • parameters:
      • event: (typeof Event)
    • source: [view]
        if(!this.disabled){
         switch(event.type){
          case "mouseenter":
          case "mouseover": // generated on non-IE browsers even though we connected to mouseenter
           this._set("hovering", true);
           this._set("active", this._mouseDown);
           break;


          case "mouseleave":
          case "mouseout": // generated on non-IE browsers even though we connected to mouseleave
           this._set("hovering", false);
           this._set("active", false);
           break;


          case "mousedown" :
           this._set("active", true);
           this._mouseDown = true;
           // Set a global event to handle mouseup, so it fires properly
           // even if the cursor leaves this.domNode before the mouse up event.
           // Alternately could set active=false on mouseout.
           var mouseUpConnector = this.connect(dojo.body(), "onmouseup", function(){
            this._mouseDown = false;
            this._set("active", false);
            this.disconnect(mouseUpConnector);
           });
           break;
         }
        }
    • summary
      Sets hovering and active properties depending on mouse state,
      which triggers _setStateClass() to set appropriate CSS classes for this.domNode.
  • dijit._CssStateMixin._setStateClass

    • type
      Function
    • source: [view]
      define("dijit/_CssStateMixin", ["dojo", "dijit"], function(dojo, dijit) {


      dojo.declare("dijit._CssStateMixin", [], {
       // summary:
       //  Mixin for widgets to set CSS classes on the widget DOM nodes depending on hover/mouse press/focus
       //  state changes, and also higher-level state changes such becoming disabled or selected.
       //
       // description:
       //  By mixing this class into your widget, and setting the this.baseClass attribute, it will automatically
       //  maintain CSS classes on the widget root node (this.domNode) depending on hover,
       //  active, focus, etc. state. Ex: with a baseClass of dijitButton, it will apply the classes
       //  dijitButtonHovered and dijitButtonActive, as the user moves the mouse over the widget and clicks it.
       //
       //  It also sets CSS like dijitButtonDisabled based on widget semantic state.
       //
       //  By setting the cssStateNodes attribute, a widget can also track events on subnodes (like buttons
       //  within the widget).


       // cssStateNodes: [protected] Object
       //  List of sub-nodes within the widget that need CSS classes applied on mouse hover/press and focus
       //.
       //  Each entry in the hash is a an attachpoint names (like "upArrowButton") mapped to a CSS class names
       //  (like "dijitUpArrowButton"). Example:
       // |  {
       // |   "upArrowButton": "dijitUpArrowButton",
       // |   "downArrowButton": "dijitDownArrowButton"
       // |  }
       //  The above will set the CSS class dijitUpArrowButton to the this.upArrowButton DOMNode when it
       //  is hovered, etc.
       cssStateNodes: {},


       // hovering: [readonly] Boolean
       //  True if cursor is over this widget
       hovering: false,

       
       // active: [readonly] Boolean
       //  True if mouse was pressed while over this widget, and hasn't been released yet
       active: false,


       _applyAttributes: function(){
        // This code would typically be in postCreate(), but putting in _applyAttributes() for
        // performance: so the class changes happen before DOM is inserted into the document.
        // Change back to postCreate() in 2.0. See #11635.


        this.inherited(arguments);


        // Automatically monitor mouse events (essentially :hover and :active) on this.domNode
        dojo.forEach(["onmouseenter", "onmouseleave", "onmousedown"], function(e){
         this.connect(this.domNode, e, "_cssMouseEvent");
        }, this);

        
        // Monitoring changes to disabled, readonly, etc. state, and update CSS class of root node
        dojo.forEach(["disabled", "readOnly", "checked", "selected", "focused", "state", "hovering", "active"], function(attr){
         this.watch(attr, dojo.hitch(this, "_setStateClass"));
        }, this);


        // Events on sub nodes within the widget
        for(var ap in this.cssStateNodes){
         this._trackMouseState(this[ap], this.cssStateNodes[ap]);
        }
        // Set state initially; there's probably no hover/active/focus state but widget might be
        // disabled/readonly/checked/selected so we want to set CSS classes for those conditions.
        this._setStateClass();
       },


       _cssMouseEvent: function(/*Event*/ event){
        // summary:
        // Sets hovering and active properties depending on mouse state,
        // which triggers _setStateClass() to set appropriate CSS classes for this.domNode.


        if(!this.disabled){
         switch(event.type){
          case "mouseenter":
          case "mouseover": // generated on non-IE browsers even though we connected to mouseenter
           this._set("hovering", true);
           this._set("active", this._mouseDown);
           break;


          case "mouseleave":
          case "mouseout": // generated on non-IE browsers even though we connected to mouseleave
           this._set("hovering", false);
           this._set("active", false);
           break;


          case "mousedown" :
           this._set("active", true);
           this._mouseDown = true;
           // Set a global event to handle mouseup, so it fires properly
           // even if the cursor leaves this.domNode before the mouse up event.
           // Alternately could set active=false on mouseout.
           var mouseUpConnector = this.connect(dojo.body(), "onmouseup", function(){
            this._mouseDown = false;
            this._set("active", false);
            this.disconnect(mouseUpConnector);
           });
           break;
         }
        }
       },


       _setStateClass: function(){
        // summary:
        //  Update the visual state of the widget by setting the css classes on this.domNode
        //  (or this.stateNode if defined) by combining this.baseClass with
        //  various suffixes that represent the current widget state(s).
        //
        // description:
        //  In the case where a widget has multiple
        //  states, it sets the class based on all possible
        //   combinations. For example, an invalid form widget that is being hovered
        //  will be "dijitInput dijitInputInvalid dijitInputHover dijitInputInvalidHover".
        //
        //  The widget may have one or more of the following states, determined
        //  by this.state, this.checked, this.valid, and this.selected:
        //   - Error - ValidationTextBox sets this.state to "Error" if the current input value is invalid
        //   - Incomplete - ValidationTextBox sets this.state to "Incomplete" if the current input value is not finished yet
        //   - Checked - ex: a checkmark or a ToggleButton in a checked state, will have this.checked==true
        //   - Selected - ex: currently selected tab will have this.selected==true
        //
        //  In addition, it may have one or more of the following states,
        //  based on this.disabled and flags set in _onMouse (this.active, this.hovering) and from focus manager (this.focused):
        //   - Disabled - if the widget is disabled
        //   - Active  - if the mouse (or space/enter key?) is being pressed down
        //   - Focused  - if the widget has focus
        //   - Hover  - if the mouse is over the widget


        // Compute new set of classes
        var newStateClasses = this.baseClass.split(" ");


        function multiply(modifier){
         newStateClasses = newStateClasses.concat(dojo.map(newStateClasses, function(c){ return c+modifier; }), "dijit"+modifier);
        }


        if(!this.isLeftToRight()){
         // For RTL mode we need to set an addition class like dijitTextBoxRtl.
         multiply("Rtl");
        }


        if(this.checked){
         multiply("Checked");
        }
        if(this.state){
         multiply(this.state);
        }
        if(this.selected){
         multiply("Selected");
        }


        if(this.disabled){
         multiply("Disabled");
        }else if(this.readOnly){
         multiply("ReadOnly");
        }else{
         if(this.active){
          multiply("Active");
         }else if(this.hovering){
          multiply("Hover");
         }
        }


        if(this._focused){
         multiply("Focused");
        }


        // Remove old state classes and add new ones.
        // For performance concerns we only write into domNode.className once.
        var tn = this.stateNode || this.domNode,
         classHash = {}; // set of all classes (state and otherwise) for node


        dojo.forEach(tn.className.split(" "), function(c){ classHash[c] = true; });


        if("_stateClasses" in this){
         dojo.forEach(this._stateClasses, function(c){ delete classHash[c]; });
        }


        dojo.forEach(newStateClasses, function(c){ classHash[c] = true; });


        var newClasses = [];
        for(var c in classHash){
         newClasses.push(c);
        }
        tn.className = newClasses.join(" ");


        this._stateClasses = newStateClasses;
    • summary
      Update the visual state of the widget by setting the css classes on this.domNode
      (or this.stateNode if defined) by combining this.baseClass with
      various suffixes that represent the current widget state(s).
  • dijit._CssStateMixin._trackMouseState

    • type
      Function
    • parameters:
      • node: (typeof DomNode)
        Should be a sub-node of the widget, not the top node (this.domNode), since the top node
        is handled specially and automatically just by mixing in this class.
      • clazz: (typeof String)
    • source: [view]
      define("dijit/_CssStateMixin", ["dojo", "dijit"], function(dojo, dijit) {


      dojo.declare("dijit._CssStateMixin", [], {
       // summary:
       //  Mixin for widgets to set CSS classes on the widget DOM nodes depending on hover/mouse press/focus
       //  state changes, and also higher-level state changes such becoming disabled or selected.
       //
       // description:
       //  By mixing this class into your widget, and setting the this.baseClass attribute, it will automatically
       //  maintain CSS classes on the widget root node (this.domNode) depending on hover,
       //  active, focus, etc. state. Ex: with a baseClass of dijitButton, it will apply the classes
       //  dijitButtonHovered and dijitButtonActive, as the user moves the mouse over the widget and clicks it.
       //
       //  It also sets CSS like dijitButtonDisabled based on widget semantic state.
       //
       //  By setting the cssStateNodes attribute, a widget can also track events on subnodes (like buttons
       //  within the widget).


       // cssStateNodes: [protected] Object
       //  List of sub-nodes within the widget that need CSS classes applied on mouse hover/press and focus
       //.
       //  Each entry in the hash is a an attachpoint names (like "upArrowButton") mapped to a CSS class names
       //  (like "dijitUpArrowButton"). Example:
       // |  {
       // |   "upArrowButton": "dijitUpArrowButton",
       // |   "downArrowButton": "dijitDownArrowButton"
       // |  }
       //  The above will set the CSS class dijitUpArrowButton to the this.upArrowButton DOMNode when it
       //  is hovered, etc.
       cssStateNodes: {},


       // hovering: [readonly] Boolean
       //  True if cursor is over this widget
       hovering: false,

       
       // active: [readonly] Boolean
       //  True if mouse was pressed while over this widget, and hasn't been released yet
       active: false,


       _applyAttributes: function(){
        // This code would typically be in postCreate(), but putting in _applyAttributes() for
        // performance: so the class changes happen before DOM is inserted into the document.
        // Change back to postCreate() in 2.0. See #11635.


        this.inherited(arguments);


        // Automatically monitor mouse events (essentially :hover and :active) on this.domNode
        dojo.forEach(["onmouseenter", "onmouseleave", "onmousedown"], function(e){
         this.connect(this.domNode, e, "_cssMouseEvent");
        }, this);

        
        // Monitoring changes to disabled, readonly, etc. state, and update CSS class of root node
        dojo.forEach(["disabled", "readOnly", "checked", "selected", "focused", "state", "hovering", "active"], function(attr){
         this.watch(attr, dojo.hitch(this, "_setStateClass"));
        }, this);


        // Events on sub nodes within the widget
        for(var ap in this.cssStateNodes){
         this._trackMouseState(this[ap], this.cssStateNodes[ap]);
        }
        // Set state initially; there's probably no hover/active/focus state but widget might be
        // disabled/readonly/checked/selected so we want to set CSS classes for those conditions.
        this._setStateClass();
       },


       _cssMouseEvent: function(/*Event*/ event){
        // summary:
        // Sets hovering and active properties depending on mouse state,
        // which triggers _setStateClass() to set appropriate CSS classes for this.domNode.


        if(!this.disabled){
         switch(event.type){
          case "mouseenter":
          case "mouseover": // generated on non-IE browsers even though we connected to mouseenter
           this._set("hovering", true);
           this._set("active", this._mouseDown);
           break;


          case "mouseleave":
          case "mouseout": // generated on non-IE browsers even though we connected to mouseleave
           this._set("hovering", false);
           this._set("active", false);
           break;


          case "mousedown" :
           this._set("active", true);
           this._mouseDown = true;
           // Set a global event to handle mouseup, so it fires properly
           // even if the cursor leaves this.domNode before the mouse up event.
           // Alternately could set active=false on mouseout.
           var mouseUpConnector = this.connect(dojo.body(), "onmouseup", function(){
            this._mouseDown = false;
            this._set("active", false);
            this.disconnect(mouseUpConnector);
           });
           break;
         }
        }
       },


       _setStateClass: function(){
        // summary:
        //  Update the visual state of the widget by setting the css classes on this.domNode
        //  (or this.stateNode if defined) by combining this.baseClass with
        //  various suffixes that represent the current widget state(s).
        //
        // description:
        //  In the case where a widget has multiple
        //  states, it sets the class based on all possible
        //   combinations. For example, an invalid form widget that is being hovered
        //  will be "dijitInput dijitInputInvalid dijitInputHover dijitInputInvalidHover".
        //
        //  The widget may have one or more of the following states, determined
        //  by this.state, this.checked, this.valid, and this.selected:
        //   - Error - ValidationTextBox sets this.state to "Error" if the current input value is invalid
        //   - Incomplete - ValidationTextBox sets this.state to "Incomplete" if the current input value is not finished yet
        //   - Checked - ex: a checkmark or a ToggleButton in a checked state, will have this.checked==true
        //   - Selected - ex: currently selected tab will have this.selected==true
        //
        //  In addition, it may have one or more of the following states,
        //  based on this.disabled and flags set in _onMouse (this.active, this.hovering) and from focus manager (this.focused):
        //   - Disabled - if the widget is disabled
        //   - Active  - if the mouse (or space/enter key?) is being pressed down
        //   - Focused  - if the widget has focus
        //   - Hover  - if the mouse is over the widget


        // Compute new set of classes
        var newStateClasses = this.baseClass.split(" ");


        function multiply(modifier){
         newStateClasses = newStateClasses.concat(dojo.map(newStateClasses, function(c){ return c+modifier; }), "dijit"+modifier);
        }


        if(!this.isLeftToRight()){
         // For RTL mode we need to set an addition class like dijitTextBoxRtl.
         multiply("Rtl");
        }


        if(this.checked){
         multiply("Checked");
        }
        if(this.state){
         multiply(this.state);
        }
        if(this.selected){
         multiply("Selected");
        }


        if(this.disabled){
         multiply("Disabled");
        }else if(this.readOnly){
         multiply("ReadOnly");
        }else{
         if(this.active){
          multiply("Active");
         }else if(this.hovering){
          multiply("Hover");
         }
        }


        if(this._focused){
         multiply("Focused");
        }


        // Remove old state classes and add new ones.
        // For performance concerns we only write into domNode.className once.
        var tn = this.stateNode || this.domNode,
         classHash = {}; // set of all classes (state and otherwise) for node


        dojo.forEach(tn.className.split(" "), function(c){ classHash[c] = true; });


        if("_stateClasses" in this){
         dojo.forEach(this._stateClasses, function(c){ delete classHash[c]; });
        }


        dojo.forEach(newStateClasses, function(c){ classHash[c] = true; });


        var newClasses = [];
        for(var c in classHash){
         newClasses.push(c);
        }
        tn.className = newClasses.join(" ");


        this._stateClasses = newStateClasses;
       },


       _trackMouseState: function(/*DomNode*/ node, /*String*/ clazz){
        // summary:
        //  Track mouse/focus events on specified node and set CSS class on that node to indicate
        //  current state. Usually not called directly, but via cssStateNodes attribute.
        // description:
        //  Given class=foo, will set the following CSS class on the node
        //   - fooActive: if the user is currently pressing down the mouse button while over the node
        //   - fooHover: if the user is hovering the mouse over the node, but not pressing down a button
        //   - fooFocus: if the node is focused
        //
        //  Note that it won't set any classes if the widget is disabled.
        // node: DomNode
        //  Should be a sub-node of the widget, not the top node (this.domNode), since the top node
        //  is handled specially and automatically just by mixing in this class.
        // clazz: String
        //  CSS class name (ex: dijitSliderUpArrow).


        // Current state of node (initially false)
        // NB: setting specifically to false because dojo.toggleClass() needs true boolean as third arg
        var hovering=false, active=false, focused=false;


        var self = this,
         cn = dojo.hitch(this, "connect", node);


        function setClass(){
         var disabled = ("disabled" in self && self.disabled) || ("readonly" in self && self.readonly);
         dojo.toggleClass(node, clazz+"Hover", hovering && !active && !disabled);
         dojo.toggleClass(node, clazz+"Active", active && !disabled);
         dojo.toggleClass(node, clazz+"Focused", focused && !disabled);
        }


        // Mouse
        cn("onmouseenter", function(){
         hovering = true;
         setClass();
        });
        cn("onmouseleave", function(){
         hovering = false;
         active = false;
         setClass();
        });
        cn("onmousedown", function(){
         active = true;
         setClass();
        });
        cn("onmouseup", function(){
         active = false;
         setClass();
        });


        // Focus
        cn("onfocus", function(){
         focused = true;
         setClass();
        });
        cn("onblur", function(){
         focused = false;
         setClass();
        });


        // Just in case widget is enabled/disabled while it has focus/hover/active state.
        // Maybe this is overkill.
        this.watch("disabled", setClass);
        this.watch("readOnly", setClass);
    • summary
      Track mouse/focus events on specified node and set CSS class on that node to indicate
      current state.   Usually not called directly, but via cssStateNodes attribute.
    • description
      Given class=foo, will set the following CSS class on the node
      - fooActive: if the user is currently pressing down the mouse button while over the node
      - fooHover: if the user is hovering the mouse over the node, but not pressing down a button
      - fooFocus: if the node is focused
      
      Note that it won't set any classes if the widget is disabled.
  • dijit._CssStateMixin._mouseDown

    • summary
  • dijit._CssStateMixin._stateClasses

    • summary
  • dijit

    • type
      Object
    • summary