使用HTML文本区域将数据绑定到每个元素

4

我正在尝试在使用d3.js v3创建的散点图上实现注释功能。当我单击每个数据点时,将出现文本框,我在其中输入文本。添加文本后,它将显示为该特定数据点的工具提示。我的实现方式如下:

eventGroup.select("circle")
    .attr("class", "dot")
    .attr("r", 4)
    .attr("cx", 10)
    .attr("cy", 10)
    .attr("fill", function(d) {
        return d.evtColor ? d.evtColor : "#229ae5";
    })
    .attr("stroke", function(d) {
        return d.evtColor ? d.evtColor : "#229ae5";
    })
    .attr("stroke-width", 2)
    .on("contextmenu", function() {
        var position = d3.mouse(this.parentNode);
        d3.select("#context-menu")
            .style("position", "absolute")
            .style("left", (d3.event.pageX - 220) + "px")
            .style("top", (d3.event.pageY - 70) + "px")
            .style("display", "inline-block")
            .on("click", function() {
                d3.select("#context-menu").style("display", "none");
                d3.select("#annotateBox")
                    .style("position", "absolute")
                    .style("left", (d3.event.pageX - 220) + "px")
                    .style("top", (d3.event.pageY - 70) + "px")
                    .style("display", "inline-block");
            });
    });

在HTML文件中,我定义了一个名为addAnnotation的按钮和一个textarea,我将在其中添加文本。
<ul id="context-menu" class="menu" style="display:none;">
    <li class="addAnnotation"><a href="">Add Annotation</a></li>
</ul>
<div id="annotateBox" style="display:none;">
   <div class="annotateInput" style="display:table-caption">
        <textarea rows="3" cols="50" maxlength="100" style="color:black" ng-model="vm.annotateText" autofocus></textarea>
        <button type="button" class="btn btn-primary pull-right" ng-click="vm.removeButton()">Done</button>
    </div>
</div>

我想做的下一步是,当单击数据点时,在弹出的文本区域中添加文本,我希望该文本绑定到特定的数据点而不是所有数据点。是否有一种方法可以将文本区域分配给调用事件而不是普遍适用于所有数据点。目前,我添加的任何文本作为一个数据点的一部分都会在其它数据点中显示。
1个回答

3

有两种方法可以设置这些值:

  1. 更改数据;
  2. 使用本地变量(仅适用于v4,不适用于您的情况)。

更改数据是最常见、最惯用的D3方法来实现您想要的效果。此外,在v3中本地变量无法使用,因此您应该使用这个解决方案:

解决方案1:更改数据

这可能是最简单和最详细记录的方法。毕竟,D3(或DDD)意味着“数据驱动文档”。

做到这一点非常容易:只需在事件监听器中使用第一个参数(传统上称为“datum”)设置任何您想要的属性即可:

selection.on("contextmenu", function(d) {
//first argument here ---------------^

例如,一个名为textArea的属性:
d3.select("textarea").node().value = d.textArea || "";
d3.select("button").on("click", function() {
    d.textArea = d3.select("textarea").node().value;
});

注意:为了使此解决方案起作用,您的datum必须是一个对象
以下是一个非常基本的示例,松散地基于您的代码。右键单击每个圆圈,输入一些内容,然后单击“完成”。在另一个圆圈中执行相同的操作,然后再次右键单击以前的圆圈:您会发现圆圈保留了数据。

var svg = d3.select("svg");
var color = d3.scaleOrdinal(d3.schemeCategory10);
var data = d3.range(5).map(function(d) {
  return {
    name: "foo"
  }
});
var circles = svg.selectAll(null)
  .data(data)
  .enter()
  .append("circle")
  .attr("r", 15)
  .attr("cy", 50)
  .attr("cx", function(_, i) {
    return 50 + 50 * i
  })
  .style("fill", function(_, i){
    return color(i);
  });
circles.on("contextmenu", function(d) {
  d3.event.preventDefault();
  d3.select("#annotateBox")
    .style("position", "absolute")
    .style("left", (d3.event.pageX - 50) + "px")
    .style("top", (d3.event.pageY + 20) + "px")
    .style("display", "inline-block");
  d3.select("textarea").node().value = d.textArea || "";
  d3.select("button").on("click", function() {
    d.textArea = d3.select("textarea").node().value;
  })
});
<script src="https://d3js.org/d3.v4.min.js"></script>
<div id="annotateBox" style="display:none;">
  <div class="annotateInput" style="display:table-caption">
    <textarea rows="3" cols="50" maxlength="100" style="color:black" autofocus></textarea>
    <button type="button">Done</button>
  </div>
</div>
<svg></svg>

由于您正在使用的是D3 v3,所以下一个解决方案现在不适用于您(更改为v4取决于您拥有的代码,可能非常容易或者可能是一场噩梦)。然而,描述局部变量的工作原理很有趣,因为该变量绑定到DOM元素,而不是更改您的数据结构:

方案2:局部变量

局部变量是在D3 v4中引入的,而且不太常见。基本上,您需要定义局部变量...

var local = d3.local();

在事件监听器内设置其值:

var thisCircle = this;
d3.select("textarea").node().value = local.get(thisCircle) || "";
d3.select("button").on("click", function() {
    local.set(thisCircle, d3.select("textarea").node().value);
});

这是演示示例:

var svg = d3.select("svg");
var color = d3.scaleOrdinal(d3.schemeCategory10);
var local = d3.local();
var data = d3.range(5).map(function(d) {
  return {
    name: "foo"
  }
});
var circles = svg.selectAll(null)
  .data(data)
  .enter()
  .append("circle")
  .attr("r", 15)
  .attr("cy", 50)
  .attr("cx", function(_, i) {
    return 50 + 50 * i
  })
  .style("fill", function(_, i) {
    return color(i);
  });
circles.on("contextmenu", function(d) {
  var thisCircle = this;
  d3.event.preventDefault();
  d3.select("#annotateBox")
    .style("position", "absolute")
    .style("left", (d3.event.pageX - 50) + "px")
    .style("top", (d3.event.pageY + 20) + "px")
    .style("display", "inline-block");
  d3.select("textarea").node().value = local.get(thisCircle) || "";
  d3.select("button").on("click", function() {
    local.set(thisCircle, d3.select("textarea").node().value);
  })

});
<script src="https://d3js.org/d3.v4.min.js"></script>
<div id="annotateBox" style="display:none;">
  <div class="annotateInput" style="display:table-caption">
    <textarea rows="3" cols="50" maxlength="100" style="color:black" autofocus></textarea>
    <button type="button">Done</button>
  </div>
</div>
<svg></svg>


感谢您提供两种解决方案。我正在尝试在我的项目中实现第一种解决方案,但由于某些原因它无法读取 d3.select("textarea").node().value = d.textArea || ""; d3.select("button").on("click", function() { d.textArea = d3.select("textarea").node().value; }); 但我正在努力调试它,我相信只是一些小错误。 - user2128

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接