Introduction

dijit/_AttachMixin allows automatic attachment of attributes and event handlers for a dijit based on DOM node content. The DOM nodes may be detached from the document, or already exist in the document. It uses data-dojo-attach-point and data-dojo-attach-event DOM node attributes to do its work.

It also allows the attach points and events for a dijit to be assigned to another object, see attachScope.

_AttachMixin can be used to instantiate dijits whose templates have been generated by a server side templating system. This places their template content in the DOM instead of the more traditional approach of baking a templateString into your dijit declaration and having them parsed during _TemplatedMixin instantiation. If the parser parses your DOM you may run into conflicts between _WidgetsInTemplateMixin and the parser, see dojo/parser (parseOnLoad) and stopParser.

This functionality is abstracted from dijit/_TemplatedMixin although historically this functionality was contained within that dijit.

Remember that content in the DOM is rendered using normal browser rendering rules. If you do not want the content to be visible until after it has been parsed or the _AttachMixin dijit otherwise created, you will have to make it invisible and handle making it visible again yourself. See visibility.

Usage

Mixin dijit/_AttachMixin when you declare your dijit and direct it to use DOM nodes that contain suitable data-dojo-attach-point or data-dojo-attach-event properties:

<div id="somenode"><span data-dojo-attach-point="anattachpoint"
     data-dojo-attach-event="click: clicked">Click me</span></div>
var MyDijit = declare([ _WidgetBase, _AttachMixin ], {
    // .. declaration goes here ..
    clicked: function(e) {
        // handle event
    }
});
// instantiate the dijit instance, which will attach to the 'somenode' div.
var mydijit = new MyDijit({}, 'somenode');
mydijit.startup();

You may also want to use to cause dijits in your DOM content, identified by having a data-dojo-type property, be parsed while your dijit is instantiated.

The _srcNodeRef of the dijit will be used as a basis for attaching.

For example, here is a working sample of the above example:

The HTML that is picked up by the dijit.

<div id="somenode"><span data-dojo-attach-point="anattachpoint"
     data-dojo-attach-event="click: clicked">Click me</span><br>
     <input data-dojo-attach-point="field"></div>

JavaScript to declare a dijit using _AttachMixin.

require([
    "dojo/_base/declare", "dojo/dom", "dijit/_WidgetBase", "dijit/_AttachMixin", "dojo/domReady!"
], function(declare, dom, _WidgetBase, _AttachMixin) {

    var MyDijit = declare([ _WidgetBase, _AttachMixin ], {
        clicked: function(e) { this.field.value = "I was clicked"; }
    })

    var mydijit = new MyDijit({}, 'somenode');
    mydijit.startup();
})

data-dojo-attach-point

(before Dojo 1.6 a.k.a. dojoAttachPoint)

In the JavaScript of a widget, one often wishes to refer to some of its html template's dom nodes directly. In this case the widget will need to access the <span> with the count in order to change the value.

You might think the widget author could just use ids in the html template, and then dom.byId() in the widget's js. But if she does, then if two or more widget instances are created, they'll all have the same ids! The dom.byId call is no longer precise enough to return the node you want.

Instead:

  1. In your widget template's html, for every node you want a variable reference for, you add the attribute: data-dojo-attach-point="yourVariableNameHere".
  2. In your widget's js, you use (without declaring them) variables for these nodes. In the example below, we access this.counter.

You don't need to declare the variables because _AttachMixin simply assigns the dom node to a property of the attachScope with attachScope[yourVariableNameHere] = refNode. Any property you have declared with that name will be overritten.

When using the widgetsInTemplate parameter, a data-dojo-attach-point on the widget node in the template will refer to the widget instance rather than the Dom Node (see also dojo/parser (parseOnLoad) and stopParser below).

data-dojo-attach-event

(before Dojo 1.6 a.k.a. dojoAttachEvent)

data-dojo-attach-event will automatically setup a connection from an event on the DOM node (onclick in this case) to call a method in the widget (in this case increment(). Multiple connections can be specified by separating them with a comma (e.g. keyup: validate, input: validate).

Here's an example of data-dojo-attach-point and data-dojo-attach-event:

 require([
     "dojo/_base/declare", "dojo/parser",
     "dijit/_WidgetBase", "dijit/_TemplatedMixin", "dojo/domReady!"
 ], function(declare, parser, _WidgetBase, _TemplatedMixin){

      declare("FancyCounter", [dijit._WidgetBase, dijit._TemplatedMixin], {
             // counter
             _i: 0,

             templateString:
                 "<div>" +
                     "<button data-dojo-attach-event='onclick: increment'>press me</button>" +
                     "&nbsp; count: <span data-dojo-attach-point='counter'>0</span>" +
                 "</div>",

              increment: function(evt){
                  this.counter.innerHTML = ++this._i;
              }
      });
      parser.parse();
});
<span data-dojo-type="FancyCounter">press me</span>

attachScope

The attachScope of _AttachMixin defaults to this, the dijit object. In some (rare) circumstances you may want to attach your attach-points and attach-events to another object. For example, when a dijit contains a nested dijit whose fields and events are considered to be 'owned' by the containing dijit, it may be laborious to set up the necessary wiring to propagate events from the inner dijit to the outer one.

In this simple example, the outer dijit has the event handler for a field that is actually created by a completely separate inner dijit.

<div id="somenode"></div>
require([
    "dojo/_base/declare", "dojo/dom", "dijit/_WidgetBase", "dijit/_TemplatedMixin", "dojo/domReady!"
], function(declare, dom, _WidgetBase, _TemplatedMixin) {

    var OuterDijit = declare([ _WidgetBase ], {
        fieldChanged: function(e) {
            this.thespan.innerHTML = "Value is now: '" + this.field.value + "'";
        }
    })
    var InnerDijit = declare([ _WidgetBase, _TemplatedMixin ], {
        templateString: "<div><span data-dojo-attach-point='thespan'>Initial span value</span><br>" +
            "<input data-dojo-attach-point='field' data-dojo-attach-event='keyup: fieldChanged'>" +
            "</div>"
    })

    // Create the outer dijit instance, and then the inner one with
    //  its attachScope referencing the outer dijit.
    var outerdijit = new OuterDijit({}, 'somenode'),
        innerdijit = new InnerDijit({
            attachScope: outerdijit
        });
    // The outer dijit is already in the DOM.  Place the inner dijit and
    //  start them both up.
    innerdijit.placeAt(outerdijit.domNode);
    outerdijit.startup();
})

Thus we achieve efficient delegation of events to the outer dijit instance.

Destruction and other lifecycle issues are not addressed in this example.

dojo/parser (parseOnLoad) and stopParser

The parser normally iterates the entire DOM and then runs through the nodes instantiating any dijits it found (that were marked with data-dojo-type). Unfortunately your _AttachMixin templates may include dijits themselves, with the intention of using _WidgetsInTemplateMixin to instantiate them. Consider the following:

<div data-dojo-type="MyDijit">
    <button data-dojo-type="dijit/form/Button" data-dojo-attach-point="mybutton">Click me</button>
</div>
require([
    "dojo/_base/declare", "dojo/dom", "dojo/parser", "dijit/_WidgetBase", "dijit/_AttachMixin", "dijit/_WidgetsInTemplateMixin", "dijit/form/Button", "dojo/domReady!"
], function(declare, dom, parser, _WidgetBase, _AttachMixin, _WidgetsInTemplateMixin) {

    var MyDijit = declare("MyDijit", [ _WidgetBase, _AttachMixin, _WidgetsInTemplateMixin ], {

        stopParser: 1,

        postCreate: function() {
            console.log("mybutton: ", this.mybutton);
        }
    })

    parser.parse();
})

When the parser scans the document, it will find both the MyDijit dijit node, and the mybutton dijit/form/Button node. It will record them both as dijits to instantiate. The MyDijit construction will happen first, and because it extends _WidgetsInTemplateMixin it will do its own scan and instantiation of dijit/form/Button. If the parser were then to continue down its array of nodes to instantiate dijits for, it would re-instantiate the dijit/form/Button.

The parser checks each constructor prototype for a 'stopParser' attribute, and does not recurse into it if found. We can use this flag on our dijit declaration to indicate to the parser that we do not want it to consider nodes in our template for parsing.

Visibility

Placing dijit template nodes in the DOM often leads to unwelcome 'flicker', where the nodes are rendered by the browser before the javascript that picks up and uses those nodes is executed. To get around this, developers often make the nodes invisible at page rendering time, and make them visible later when required. There are various common schemes used to make nodes invisible. Which you use it up to you.

  • Set the CSS style "display" to "none" (either by an inline style or a CSS class). Similarly you could set "visibility" to "hidden" but this would make the nodes take up space which is normally not desired.
  • Set the position of the element to somewhere outside the expected viewport of the browser window.
  • Set the size (height, width) of the nodes to zero.
  • Set the position and z-index so they are rendered underneath other elements (e.g. a large blocking element).

Because the options are so varied, and each option brings its own challenges (e.g. dijit/Editor has serious problems if created in a hidden element) no attempt is made by Dojo to prescribe how you should do it.