Custom JavaScript Controls#

The Python and R bindings cover the common options, but every figure they produce is a live LightGraph instance with a full JavaScript API — you can wire your own buttons, dashboards, or page logic to it. This page shows how to reach the instance from each host and what you can do with it.

The instance API#

Whichever way a graph was created, its container element carries the instance as container.lightgraph (in hand-written JavaScript you also get it back from new lightGraph.LightGraph(...)). The instance offers:

  • MethodssetData({nodes, edges}), updateConfig(partial), zoomToFit(padding?), getSelection(), selectNodes(ids), egoFilter(nodeId, depth?) / clearEgoFilter() / getVisibleNodes(), settle(ticks?), resize(), destroy().

  • Eventson(event, cb) / off(event, cb) for nodeClick ({node}), selectionChange (array of selected nodes), dataLoad, egoFilter, and destroy.

Live example#

The toolbar above this graph is plain HTML — three buttons and a status label — talking to the instance. Click a node or use the buttons:

The wiring is just event handlers calling instance methods:

var graph = new lightGraph.LightGraph(document.getElementById('graph'), {
  nodes: nodes, edges: edges, config: config
});
var status = document.getElementById('status');

document.getElementById('btn-starks').onclick = function () {
  graph.clearEgoFilter();
  graph.selectNodes(['Eddard', 'Catelyn', 'Robb', 'Sansa',
                     'Arya', 'Bran', 'Rickon', 'Jon']);
};
document.getElementById('btn-tyrion').onclick = function () {
  graph.egoFilter('Tyrion', 2);
};
document.getElementById('btn-reset').onclick = function () {
  graph.clearEgoFilter();
  graph.selectNodes([]);
  graph.zoomToFit();
};

graph.on('nodeClick', function (e) {
  status.textContent = 'Clicked ' + e.node.id;
});
graph.on('selectionChange', function (selected) {
  if (selected.length) status.textContent = selected.length + ' selected';
});
graph.on('egoFilter', function () {
  status.textContent = graph.getVisibleNodes().length + ' nodes visible';
});

(The full page is generated by docs/tools/gen_example_embeds.py if you want to copy it as a starting point.)

From R: htmlwidgets::onRender#

R’s htmlwidgets framework has a standard hook for attaching JavaScript to a rendered widget. The hook receives the widget’s DOM element, so the instance is one property away:

library(lightgraph)
library(htmlwidgets)

data(got)
w <- lightgraph(edges = got, node_metric = lg_pagerank(got),
                edge_weight_to_width = TRUE)
onRender(w, "
  function(el, x) {
    var graph = el.lightgraph;
    graph.on('nodeClick', function (e) {
      console.log('clicked', e.node.id);
    });
    graph.egoFilter('Tyrion', 2);
  }
")

This works in the RStudio viewer, in R Markdown documents, and in Shiny (wrap the widget in renderLightgraph() as usual — the onRender hook travels with it, and nodeClick handlers can call Shiny.setInputValue() to send selections back to the server).

From Python: extend the generated page#

net_vis() returns a NetworkVisualization whose .html property is a complete, self-contained page. Append your own script before saving it. One detail: the page initializes the graph asynchronously, so poll until the instance appears on the container (id lightGraph):

from lightgraph import net_vis, datasets, pagerank

edges = datasets.got()
vis = net_vis(edges=edges, node_metric=pagerank(edges))

controls = """
<script>
(function poll() {
  var el = document.getElementById('lightGraph');
  if (!(el && el.lightgraph)) return setTimeout(poll, 50);
  var graph = el.lightgraph;
  graph.on('nodeClick', function (e) {
    console.log('clicked', e.node.id);
  });
  graph.egoFilter('Tyrion', 2);
})();
</script>
"""
html = vis.html.replace('</body>', controls + '</body>')
with open('got_custom.html', 'w') as f:
    f.write(html)

Inside Jupyter, vis renders in a sandboxed iframe for isolation, so scripts on the notebook page cannot reach it — the pattern above (augment the HTML, then save or serve it) is the supported route for custom controls.

Going further#

updateConfig(partial) accepts any fragment of the JS DEFAULT_CONFIG (see API Reference), so a slider wired to graph.updateConfig({edges: {widthScale: v}}) gives you live styling controls; setData({nodes, edges}) swaps the whole graph and reuses the instance. Everything the built-in toolbar and sidebar do goes through this same public API.