在单击的文本元素上绘制箭头?

Drawing an arrow on clicked text elements?

我正在尝试使单个 words/phrases 可点击,一旦两个 words/phrases 被 selected,提供一个选项来从一个词到其他。我怎样才能在 d3 中实现这一点?

这是一个 fiddle 文本 selection 可以工作的地方(只需 select 文本跨度或双击单词)。现在我需要做的就是在 selected 文本元素上启用 'click' 并绘制一个箭头从一个到另一个。有什么建议吗?

目前的代码如下:

body = d3.select('body')

svg = body.append('svg')
            .attr('height', 600)
            .attr('width', 600);

var g = svg.append('g').attr("transform" ,"scale(0)");

rect = g.append('rect')
        .attr('width', 300)
        .attr('height', 300)
        .attr('x', 40)
        .attr('y', 100)
        .style('fill', 'none')
        .attr('stroke', 'black')

text = g.append('foreignObject')
        .attr('x', 50)
        .attr('y', 130)
        .attr('width', 280)
        .attr('height', 280)
        .append("xhtml:body")
        .html('<p style="width: 280px;">This is some information about whatever where I want two words to be clickable individually and then provide a way to drag from one word to another to draw an arrow</p>').on('click', function() {
           console.log(d3.select(this).text()); 
        });

g.transition().duration(500).attr("transform" ,"scale(1)");

d3.selectAll("p").on("mouseup", checkSelection)
                 .on("keyup", checkSelection);

function checkSelection(d,i) {
    var selection = document.getSelection();

    if (selection.isCollapsed) return;

    var range = selection.getRangeAt(0);
    highlightRange(range);
}

function highlightRange(range) {
    var newNode = document.createElement("div");
    newNode.setAttribute(
       "style",
       "background-color: yellow; display: inline;"
    );
    range.surroundContents(newNode);
}

使用 d3 拖动行为在选择之间绘制连接线。为绘制连接路径创建一个虚拟路径,如果拖动在选择处结束则绘制一条线。

JS

svg.append("defs").append("marker")
    .attr("id", "arrowhead")
    .attr("refX", 9)
    .attr("refY", 2)
    .attr("markerWidth", 6)
    .attr("markerHeight", 4)
    .attr("orient", "auto")
    .append("path")
    .attr("d", "M 0,0 V 4 L6,2 Z");

var dummy = svg.append("line")
    .attr("class","dummy")
    .style("display","none")
    .attr("marker-end", "url(#arrowhead)");

var drag = d3.behavior.drag()
    .on("dragstart", function(){   
       var x= d3.event.sourceEvent.x, y= d3.event.sourceEvent.y;
       dummy.attr("x1",x)
        .attr("y1",y).attr("x2",x)
        .attr("y2",y)
         .style("display","block");                        
    })
    .on("drag", function(d,i) {
       var x= d3.event.sourceEvent.x, y= d3.event.sourceEvent.y;
       dummy.attr("x2",x)
        .attr("y2",y)
    })
   .on("dragend",function(){
       var isSelected = d3.select(d3.event.sourceEvent.target).classed("selected");
       if(isSelected && d3.event.sourceEvent.target!=this){
           var x1=dummy.attr("x1"), y1=dummy.attr("y1"), 
               x2=dummy.attr("x2"), y2=dummy.attr("y2");
           svg.append("line")
           .attr("class","connector")
           .attr("x1",x1)
           .attr("y1",y1)
           .attr("x2",x2)
           .attr("y2",y2)
           .attr("marker-end", "url(#arrowhead)");             
       }
       dummy.style("display","none");
   });

function highlightRange(range) {
    var newNode = document.createElement("div");
    d3.select(newNode).attr("class","selected").call(drag);
    range.surroundContents(newNode);
}

样式

::selection {
    background-color:yellow;
}
.dummy{
    stroke: black;
    fill:none;
    stroke-width: 2px;
    stroke-dasharray: 5 5;
}
.connector{
    stroke: steelblue;
    fill:none;   
    stroke-width: 2px;
}
.selected{
   background-color: yellow;
   display: inline; 
}

已更新JSFiddle