HTML Interaction

This intro page explains how to use GoJS Diagrams alongside other HTML elements in a webapp.

For custom Text Editors, Context Menus, and ToolTips, which are invoked and hidden via GoJS tool operations, it is best to use the HTMLInfo class. HTMLInfo is described in the second section of this page.

Using HTML Alongside GoJS

Editing Parts with the HTML Data Inspector

Generally, GoJS can interact with the rest of the page via JavaScript that programatically moves and modifies GoJS objects and the Diagram. If you have not read about programatically interacting with Parts and the Model, there is a GraphObject Manipulation tutorial for this purpose.

To help programmers get started with HTML controls we have implemented a simple Data Inspector Extension, an HTML-based property editor that displays and allows editing of data for the selected Part.

The Data Inspector chiefly works via a "ChangedSelection" Diagram Listener. When triggered, it populates HTML Fields. Editing those fields and clicking away then update the selected Part by calling diagram.model.setDataProperty to update the model.

jQuery and GoJS

GoJS does not depend on jQuery, but the two can be used together. The Tabs Sample shows how to use GoJS inside a jQuery tab. The HTML Interaction Sample places a GoJS Palette inside of a jQuery movable window, and a data inspector that modifies the current selected node inside another.

jQuery normally sets the $ variable. If you are copying code from our samples or documentation, be aware that we usually do this: var $ = go.GraphObject.make; so that uses of $ in our examples will build GraphObjects and other GoJS objects. Caution: calling jQuery when trying to build GoJS objects will cause unusual and cryptic errors. So you should locally assign the $ variable or use a different variable for building GoJS objects.

HTML Focus on Diagrams

When browser elements are focused, they are scrolled into view as much as possible, which may be unwelcome in some web apps. You can disable this effect by overriding Diagram.doFocus.

// Save and restore the window scroll values
// so that the browser does not modify the window scroll when a Diagram is focused
myDiagram.doFocus = function() {
  var x = window.scrollX || window.pageXOffset;
  var y = window.scrollY || window.pageYOffset;
  go.Diagram.prototype.doFocus.call(this);
  window.scrollTo(x, y);
}

You can remove the outline while the Diagram is in focus. This is a CSS effect, not a GoJS effect, and can be removed by removing the CSS outline from all canvas elements inside the Diagram div:

/* affect all canvas elements inside myDiagramDiv */
#myDiagramDiv canvas {
  outline: none;
  -webkit-tap-highlight-color: rgba(255, 255, 255, 0); /* mobile webkit */
}

If you use no other HTML Canvas elements on your page, you could do this for all canvas elements, so that it will affect all Diagrams, instead of just the one within a particular DIV ID.

The HTMLInfo Class

Use the HTMLInfo class to display custom HTML page elements, such as a context menu, tooltip, or text editor made of HTML.

Properties that can be set to an instance of HTMLInfo include:

Usage

When replacing GoJS functionality with custom functionality, the main concern is when to show and hide the custom content. HTMLInfo does this with two settable functions defined by the programmer and called by GoJS:

In lieu of setting HTMLInfo.hide, you can set the HTMLInfo.mainElement property to the primary HTML Element that you are showing/hiding, and HTMLInfo will automatically hide the provided element by calling:

mainElement.style.display = "none";

HTMLInfo samples

Tooltips

For tooltips, if a GraphObject.toolTip or Diagram.toolTip is set to an instance of HTMLInfo, GoJS calls HTMLInfo.show in ToolManager.showToolTip. After the tooltip delay, GoJS will call HTMLInfo.hide in ToolManager.hideToolTip.

What follows is an example using HTMLInfo.show and HTMLInfo.hide, but the HTMLInfo.hide is simple enough that setting the HTMLInfo.mainElement to the tooltip div instead would be sufficient.

  function showToolTip(obj, diagram, tool) {
    var toolTipDIV = document.getElementById('toolTipDIV');
    var pt = diagram.lastInput.viewPoint;
    toolTipDIV.style.left = (pt.x + 10) + "px";
    toolTipDIV.style.top = (pt.y + 10) + "px";
    document.getElementById('toolTipParagraph').textContent = "Tooltip for: " + obj.data.key;
    toolTipDIV.style.display = "block";
  }

  function hideToolTip(diagram, tool) {
   var toolTipDIV = document.getElementById('toolTipDIV');
   toolTipDIV.style.display = "none";
  }

  var myToolTip = $(go.HTMLInfo, {
    show: showToolTip,
    hide: hideToolTip
    /*
      since hideToolTip is very simple,
      we could have set mainElement instead of setting hide:
    mainElement: document.getElementById('toolTipDIV')
    */
  });

  diagram.nodeTemplate =
    $(go.Node, "Auto",
      {
        toolTip: myToolTip
      },
      $(go.Shape, "RoundedRectangle", { strokeWidth: 0},
        new go.Binding("fill", "color")),
      $(go.TextBlock,
        { margin: 8 },
        new go.Binding("text", "key"))
    );

  diagram.model = new go.GraphLinksModel(
  [
    { key: "Alpha", color: "lightblue" },
    { key: "Beta", color: "orange" },
    { key: "Gamma", color: "lightgreen" },
    { key: "Delta", color: "pink" }
  ]);
  <!-- this must be added as a sibling of the Diagram -->
  <div id="toolTipDIV" style="position: absolute; background: white; z-index: 1000; display: none;">
    <p id="toolTipParagraph">Tooltip
  </div>

Context Menus

For context menus, ContextMenuTool.showContextMenu will call HTMLInfo.show. ContextMenuTool.hideContextMenu will call HTMLInfo.hide.

// Assign an HTMLInfo to the Diagram:
myDiagram.contextMenu = $(go.HTMLInfo, {
  show: showContextMenu,
  hide: hideContextMenu
});

function showContextMenu(obj, diagram, tool) {
  // Show the context menu HTML element:
  SomeDOMElement.style.display = "block";

  // Also show relevant buttons given the current state
  // and the GraphObject obj; if null, the context menu is for the whole Diagram
}

function hideContextMenu() {
  SomeDOMElement.style.display = "none";
}

function buttonClick() {
  // do some action when a context menu button is clicked

  // then:
  myDiagram.currentTool.stopTool();
}

Text Editors

For custom text editors, TextEditingTool.doActivate will call HTMLInfo.show. TextEditingTool.doDeactivate will call HTMLInfo.hide.

HTMLInfos used as text editors must also define a HTMLInfo.valueFunction. When TextEditingTool.acceptText is called, GoJS will call HTMLInfo.valueFunction and use the return value as the value for the TextEditingTool completion.

The example below constructs an HTMLInfo that uses HTMLInfo.show and HTMLInfo.hide to dynamically add, populate, and remove HTML elements from the page.

// Diagram setup. The HTMLInfo is set at the end of this code block.
diagram.nodeTemplate =
  $(go.Node, "Auto",
    $(go.Shape, "RoundedRectangle", { strokeWidth: 0},
      new go.Binding("fill", "color")),
    $(go.TextBlock,
      { editable: true, margin: 8, choices: ['Alpha', 'Beta', 'Gamma', 'Delta'] },
      new go.Binding("text"))
  );

diagram.model = new go.GraphLinksModel(
[
  { text: "Alpha", color: "lightblue" },
  { text: "Beta",  color: "orange" },
  { text: "Gamma", color: "lightgreen" },
  { text: "Delta", color: "pink" }
]);

// Create an HTMLInfo and dynamically create some HTML to show/hide
var customEditor = new go.HTMLInfo();
var customSelectBox = document.createElement("select");

customEditor.show = function(textBlock, diagram, tool) {
  if (!(textBlock instanceof go.TextBlock)) return;

  // Populate the select box:
  customSelectBox.innerHTML = "";

  // this sample assumes textBlock.choices is not null
  var list = textBlock.choices;
  for (var i = 0; i < list.length; i++) {
    var op = document.createElement("option");
    op.text = list[i];
    op.value = list[i];
    customSelectBox.add(op, null);
  }

  // After the list is populated, set the value:
  customSelectBox.value = textBlock.text;

  // Do a few different things when a user presses a key
  customSelectBox.addEventListener("keydown", function(e) {
    var keynum = e.which;
    if (keynum == 13) { // Accept on Enter
      tool.acceptText(go.TextEditingTool.Enter);
      return;
    } else if (keynum == 9) { // Accept on Tab
      tool.acceptText(go.TextEditingTool.Tab);
      e.preventDefault();
      return false;
    } else if (keynum === 27) { // Cancel on Esc
      tool.doCancel();
      if (tool.diagram) tool.diagram.focus();
    }
  }, false);

  var loc = textBlock.getDocumentPoint(go.Spot.TopLeft);
  var pos = diagram.transformDocToView(loc);
  customSelectBox.style.left = pos.x + "px";
  customSelectBox.style.top  = pos.y + "px";
  customSelectBox.style.position = 'absolute';
  customSelectBox.style.zIndex = 100; // place it in front of the Diagram

  diagram.div.appendChild(customSelectBox);
}

customEditor.hide = function(diagram, tool) {
  diagram.div.removeChild(customSelectBox);
}

// This is necessary for HTMLInfo instances that are used as text editors
customEditor.valueFunction = function() { return customSelectBox.value; }

// Set the HTMLInfo:
diagram.toolManager.textEditingTool.defaultTextEditor = customEditor;