简体   繁体   中英

How to have only some edges react to `mouseover` in a D3 v4 force graph

I am working on this force graph in D3 v4.

When a user click on a nodes, only the nodes connected to it become visible. Moreover, the edges connecting those nodes become thicker and users can hover on the so see more information (the tooltip that appears on the right).

This is how I highlight the connected nodes after a click

//Highlight on click
function highlighting () {
//Toggle stores whether the highlighting is on
var node = d3.selectAll('circle');
var link = d3.selectAll('line');
var toggle = 0;
//Create an array logging what is connected to what
var linkedByIndex = {};
for (i = 0; i < dataset.nodes.length; i++) {
    linkedByIndex[i + "," + i] = 1;
};
d3.selectAll('line').each(function (d) {
    linkedByIndex[d.source.index + "," + d.target.index] = 1;
});
//This function looks up whether a pair are neighbours
function neighboring(a, b) {
    return linkedByIndex[a.index + "," + b.index];
}
function connectedNodes() {
    if (toggle == 0) {
        //Reduce the opacity of all but the neighbouring nodes
        d = d3.select(this).node().__data__;
        node.style("opacity", function (o) {
            return neighboring(d, o) | neighboring(o, d) ? 1 : 0.0;
        });
        link.style("opacity", function (o) {
            return d.index==o.source.index | d.index==o.target.index ? 1 : 0.0;
        });
        link.attr('stroke-width' , 4);
        toggle = 1;
        interactivityHighlight();

        //Change navigation div
        d3.select('#click01').classed('hidden', true);
        d3.select('#click02').classed('hidden', false);

    } else {
        //Put them back to starting opacity
        node.style("opacity", 1);
        link.style("opacity", function (d) {return edgeOpacityScale(d.collaborations);});
        link.attr('stroke-width', 1);
        link.attr('class', null);
        toggle = 0;
        //Change navigation
        d3.select('#click01').classed('hidden', false);
        d3.select('#click02').classed('hidden', true);
    }
}
node.on('click', connectedNodes);
}

And this is the function I call after the click

function interactivityHighlight () {
graph.selectAll('line').on('mouseover', function (d) {
    if (d3.select(this).style('opacity') == 1) {
        d3.select(this)
        .attr('stroke', 'red')
        .attr('stroke-width', 6);

        d3.select('#tooltip')
        .classed('hidden', false);

        d3.select('#tooltip')
        .append('p')
        .attr('id', 'org_names')
        .text('Collaborations between ' + d.source.name + ' and ' + d.target.name);

        d3.select('#tooltip')
        .append('p')
        .attr('id', 'collaborations')
        .text('Worked together on ' + d.collaborations + ' projects');

        d3.select('#tooltip')
        .append('p')
        .attr('id', 'collBudget')
        .text('Total budget: '+ commafy(d.collBudget));
}})

graph.selectAll('line').on('mouseout', function (d) {
    if (d3.select(this).style('opacity') == 1) {
        d3.select(this)
        .attr('stroke', 'black')
        .attr('stroke-width', 4);

        d3.select('#tooltip')
        .selectAll('p')
        .remove();

        d3.select('#tooltip')
        .classed('hidden', true);
}})
}

Basically all non-connected nodes get opacity=0 and thus become invisible. However, they are still present on the graph: hovering over a line may not trigger interactivityHighlight() because the mouse is actually hovering over an invisible edge.

Is there a way I can make the invisible edges really disappear, or make the visible ones "get on top" of all the others?

Add a css class that includes this pointer-events rule eg:

.hidden {
  pointer-events: none;
}

And then set that class on the links

.classed("hidden", function(d) {
    /* return true/false, decide using the same logic you use for opacity */ 
});

Links with the hidden class will let pointer events pass through to anything underneath

Use the same rule that you used to define "opacity" for defining "pointer-events":

link.attr("pointer-events", function (o) {
        return d.index==o.source.index | d.index==o.target.index ? "all" : "none";
    });

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM