How do I create interactive elements that appear on hover?

I am showing svg elements - let's call them tags - on mouseover events on graph nodes. Show / hide this is pretty nice, although it may have improved. However, I would like the items displayed on the mouse to be clickable.

Currently, mouseover events are not captured on tags. If I disable the mouseout event, which destroys the tags, the events on the tags are captured correctly, but obviously the tags remain forever.

Is there a way for tags to be clickable and destroyed on mouse events on the parent? I was thinking about adding a 1 second delay before removing tags, but not sure how to do it, and if it is correct.

The code below shows an example function - script here:

// graph size
var width = 400;
var height = 400;

var nodes = [{name: 'A'}, {name: 'B'}, {name: 'C'}, {name: 'D'}];
var edges = [{source: 'A', target: 'B'}, {source: 'B', target: 'C'}, {source: 'C', target: 'A'}, {source: 'C', target: 'D'}];
var tags = [10, 35, 56, 9];

var nodeMap = {};
nodes.forEach(function (x) {nodeMap[] = x;});
var links = (x) {
    return {source: nodeMap[x.source], target: nodeMap[], value: 1};

var svg ="body").append("svg")
.attr("width", width)
.attr("height", height)
.attr("pointer-events", "all")
.call(d3.behavior.zoom().on("zoom", redraw))

var force = d3.layout.force()
.size([width, height]);

var stdDragStart = force.drag().on("dragstart.force");
.on("dragstart", function (d) {
//prevent dragging on the nodes from dragging the canvas
d3.event.sourceEvent.stopPropagation();, d);


var link = svg.selectAll(".link")
.attr("class", "link");

var node = svg.selectAll(".node")
.attr("class", "node")
.attr("id", function (d) {
.on("dblclick", dblclick)
.on("mouseover", function (d) {
.on("mouseout", function (d) {"#" +".tool").remove();

.attr("class", "circle")
.attr("r", 40);


// display name in nodes if node structure
.attr("text-anchor", "middle")
.attr("dy", ".35em")
.text(function (d) {

force.on("tick", function () {
link.attr("x1", function (d) {return d.source.x;})
    .attr("y1", function (d) {return d.source.y;})
    .attr("x2", function (d) {return;})
    .attr("y2", function (d) {return;});
node.attr("transform", function (d) {
        return "translate(" + d.x + "," + d.y + ")";

// redraw after zooming
function redraw() {
    svg.attr("transform", "translate(" + d3.event.translate + ")" + " scale(" + d3.event.scale + ")");

function dblclick(d) {"fixed", d.fixed = false);

function dragstart(d) {"fixed", d.fixed = true);

function drawTags(tags, onto) {

    var rScale = d3.scale.ordinal()
    .rangeBands([-Math.PI, Math.PI]);

    var local ="#" + onto).selectAll(".tool")
    .attr("pointer-events", "all")
    .attr("class", "tool")
    .attr("transform", function (d) {
    var x = Math.sin(rScale(d)) * 40;
    var y = Math.cos(rScale(d)) * 40;
    return "translate(" + x + "," + y + ")";
    .on("mouseover", function (d) {
    alert("mouse over tag " + d);
    .attr("class", "circle")
    .attr("r", 15);

    .attr("text-anchor", "middle")
    .attr("dy", ".35em")
    .text(function (d) {
    return d


enter image description here


source to share

1 answer

The partial answer is to add a delay before removing tags as suggested above. This can be done using a node variable with:

.on("mouseout", function(d) {"#"".tool")


It's also better to reduce the overlap between tags and the parent node (in drawTags), because the mouse pointer is only on the parent node, the tags are considered.

var x = Math.sin(rScale(d))*50; // vs. 40px previously
var y = Math.cos(rScale(d))*50;


Change the mouseover event on the tags to a click event and make the tags and text fetch only the arrow-tags with .attr ("pointer-events", "click")

.on("click", function(d) { alert(d); });




All Articles