Select to view content in your preferred language

On binding dijit/Menu to a graphic

3048
2
11-25-2013 12:11 PM
BenFousek
Regular Contributor
I recently ran into some trouble binding menus to graphics and this is what I learned.

//create graphic
if (result.geometry.type != 'extent') {
  var graphic = new Graphic(result.geometry);
} else {
  var graphic = this._extentToPolygon(result.geometry);
}

//add graphic
this.layer.add(graphic);

//graphic menu
graphic.menu = new Menu({
  contextMenuForWindow: false,
  leftClickToOpen: false
});

//add some menu items
graphic.menu.addChild(new MenuItem({
  label: 'Move to Front',
  onClick: lang.hitch(this, function() {
    graphic.getDojoShape().moveToFront();
  })
}));
graphic.menu.addChild(new MenuItem({
  label: 'Move to Back',
  onClick: lang.hitch(this, function() {
    graphic.getDojoShape().moveToBack();
  })
}));

//bind the menu to graphics dom node
graphic.menu.bindDomNode(graphic.getDojoShape().getNode());


I was creating graphics, each with a custom menu based on geometry type and such, then binding the menu to the graphics dom node.  The menu was initially bound to graphic's dom node, however any number of actions appear to magically unbind the menu.  These include, but are not limited to, panning/zooming the map and editing the graphic's symbol or geometry.  Looking for answers led me to explore regions of the jsapi I had never ventured into before, as well as, getting to know dojox/gfx. GFX being the vector graphics api that powers esri graphics and graphic layer types.

Turns out there there is a lot going on with graphics and graphic layers; way more than I had expected.  While the layer object contains all of its graphics in the DOM, a graphic may not have a dom node in the GFX surface.  This makes sense when you think about a layer being hidden or a graphic being outside the extent of the map (the GFX surface).  The jsapi is destroying and creating shapes (vector objects) whenever a layer refreshes or a graphic's geometry/style properties are updated.  As a result the initial dom node the menu was bound to no longer exists.

So how to keep the menu bound to the dom node which is currently representing our graphic?  My first thought was to rebind the menu when updating the geometry and style, and by iterating through graphics on layer events, which seemed excessive and messy.  I ended up using the layer's "mouse-over" event to bind the menu to whatever dom node is representing the graphic.  After having spent a day diagnosing the problem and coming up with a solution I happened upon the Display context menu in the samples.  The sample also uses the "mouse-out" event to unbind the menu, which is best practice for dom nodes that may be destroyed.

on(this.layer, 'mouse-over', function(evt) {
  evt.graphic.menu.bindDomNode(evt.graphic.getDojoShape().getNode());
});
on(this.layer, 'mouse-out', function(evt) {
  evt.graphic.menu.unBindDomNode(evt.graphic.getDojoShape().getNode());
});


In conclusion: 1) Always check the samples. There's a chance it may save you time and hassle.  2) Check out dojox/gfx. The demos and test folders here are great for learning about GFX and will enlighten even long time jsapi developers.
0 Kudos
2 Replies
JasonZou
Regular Contributor
Nice summary, Ben. Thank you very much for taking time to share your experience.
0 Kudos
JianHuang
Occasional Contributor III
Great job, Ben.
Just a note, you can always call graphic.getNode() to get the raw DOM element, which could be SVG or canvas depending on browsers. Calling getDojoShape() is a good idea since dojo is doing a great job to eliminate the discrepancies among browsers. I totally agree with you, explore GFX and take advantage of it whenever you can. Manipulating DOM element directly is very hard and error-prone.
0 Kudos