d3js在exit()之后输入()。append.remove()

时间:2015-09-03 06:28:31

标签: javascript d3.js

我有一个可以按日期范围和情绪过滤的文字云。有时会有更多的数据,有时候会有更少的数据。当我删除数据时,更新dom然后添加数据,删除的元素不会再回来。使用d3js版本3.4.13



 var width = 600, height = 200;
    var words = ["Hello", "world", "Wonderful"];
    //inserting text
    var wcwords = d3.select("#wordcloud")
        .attr("width", width)
        .attr("height", height)
        .append("g")
        .attr("transform", "translate(" + width / 2 + "," + height / 2 + ")")
        .selectAll("text")
        .data(words)
        .enter()
        .append("text");

    wcwords
        .attr("transform", function(d,i) {
            return "translate(" + [5, 20*i] + ")";
        })
        .text(function(d) { return d; });
    //changing data and updating dom (in this change there are less items)
    wcwords.data(words.slice(0,2)).exit().remove();
    //changing data and updating dom (in this change there are more items)
    wcwords.data(words.concat(["No"])).enter().append('text')
        .attr("transform", function(d,i) {
            return "translate(" + [5, 20*i] + ")";
        })
        .text(function(d) { return d; });

<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<svg id='wordcloud'></svg>
&#13;
&#13;
&#13;

修改

原始代码无法正常工作更新了我的帖子,其代码可以满足我的需求。新增,删除和更新的项目动画不同。我可以更改现有项目,删除项目并再次返回项目。

诀窍是使用正确的选择parent.selectAll(children)并传递更新对象(.data(newData)返回的对象)

这是&#34;工作&#34;代码,希望我做得正确:

&#13;
&#13;
var width = 600;
var height = 200;
var words = ["Hello", "world", "Wonderful"];
var when=1000;
var step=1;
//this function sets the data and passes the update object
//  to exit, update and enter
function change(data){
  var update =   d3.select('#wccontainer')
    .selectAll('text')
    .data(data);
  exitWords(update);
  updateWords(update);
  enterWords(update);
}
//existing items move to the right
function updateWords(update){
  update
    //this is an existing item, no need for append
    .text(function(d) { return d; })
    .transition()
    .duration(when-100)
    .attr("transform", function(d,i) {
      this.left=this.left+25;
      return "translate(" + [this.left, 20*i] + ")";
    })
    .style('opacity',1);
}
//new items fade in
function enterWords(update){
  update
    .enter()
    .append("text")
    .attr("transform", function(d,i) {
      this.left=0;
      return "translate(" + [5, 20*i] + ")";
    })
    .text(function(d) { return d; })
    .style('opacity',0)
    .transition()
    .duration(when-100)
    .attr("transform", function(d,i) {
      return "translate(" + [5, 20*i] + ")";
    })
    .style('opacity',1);
}
//removed words fade out
function exitWords(update){
  var removeItems = update
    .exit()
  removeItems
    .transition()
    .duration(when-800)
    .style('opacity',0)
    .each('end',function(){
      removeItems.remove();
    });
}
function later(when,fn,parms){
  setTimeout(function(){
    fn.apply(null,parms);
  },when);
}
//create the g container and set svg width/height
d3.select("#wordcloud")
  .attr("width", width)
  .attr("height", height)
  .append("g")
  .attr('id','wccontainer')
  .attr("transform", "translate(" + width / 2 
      + "," + height / 2 + ")")
//set the text labels
change(words);
//in 1000ms (value of when) set the text lables with changed data
later(when,change,[words.slice(0,2)]);
//in 2000ms  set the text lables with changed data
later(when*++step,change,[["CHANGED"]
  .concat(words.slice(1,2))
  .concat(["ONE","TWO","THREE","FOUR"])]);
//in 3000ms  set the text lables with the original values
later(when*++step,change,[words]);
&#13;
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<svg id='wordcloud'></svg>
&#13;
&#13;
&#13;

1 个答案:

答案 0 :(得分:2)

我先说明首先发生的事情......

var width = 600, height = 200;
var words = ["Hello", "world", "Wonderful"];

var wcwords = d3.select("#wordcloud")
    .attr("width", width)
    .attr("height", height)
    .append("g")
    .attr("transform", "translate(" + width / 2 + "," + height / 2 + ")")
    .selectAll("text")
    .data(words);
    .enter()
    .append("text");  

wcwords现在是一个输入选择,恰好与更新集合具有相同的结构,因为所有元素都是新的。由于使用了selectAll,因此选择嵌套在g节点下:这是选择的父对象。

wcwords
    .attr("transform", function(d,i) {
        return "translate(" + [5, 20*i] + ")";
    })
    .text(function(d) { return d; });

wcwords.data(words.slice(0,2)).exit().remove();  

所有这一切都是使用data方法作为选择器来删除一个DOM元素。新选择(仅包含两个元素)未在范围内引用,wcwords 未更改,因此实际上DOM现在与选择不同步。

wcwords.data(words.concat(["No"])).enter().append('text')
    .attr("transform", function(d,i) {
        return "translate(" + [5, 20*i] + ")";
    })
    .text(function(d) { return d; });  

创建新选择,同样wcwords对象不变。将wcwords(不是DOM结构)的节点结构与新数据结构进行比较,因为前者有3个节点,后者有4个节点,因为data保留了索引,所以输入选择将由一组4个元素组成,前三个元素null,最后一个元素是新节点的基准对象。然后,通过wcwords语句将新文本节点添加到gappend)的父节点的末尾。由于第三个元素不在enetr选择中,因此不会重新插入。

基本原则是

  1. data不会更改调用的对象,它会返回对新选择的引用(此处将被忽略)
  2. data语句在构造输入,更新和退出选择时将选择结构与数据结构进行比较。它不与DOM结构进行比较。
  3. 我猜测你所期望的顺序,因为你还没有分享,但也许你会选择以下内容。

    &#13;
    &#13;
    var width = 70, height = 100;
        var words = ["Hello", "world", "Wonderful"];
        var outputLog = OutputLog("#output-log");
        var transitionLog = OutputLog("#transition-log");
    
        var wcwords = d3.select("#wordcloud").style("display", "inline-block")
            .attr("width", width)
            .attr("height", height)
            .append("g")
            .style("font-size", "10px")
            .attr("transform", "translate(" + 10 + "," + 20 + ")")
            .selectAll("text")
            .data(words)
            .enter()
            .append("text")
            .style("opacity", 0);
    
        wcwords
            .text(function(d) { return d; })
            .attr("transform", function(d,i) {
                return "translate(" + [5, 20*i] + ")";
            })
            .call(step, 0, "in")
            .call(log, "wcwords.data(words) enter");
    
        // bind a new data set to the selection and return the update selection
        var wcwords = wcwords.data(words.slice(0,2))
            .call(log, "wcwords.data(words.slice(0,2)) update");
    
        // merge the enter selection into the update selection and update the DOM
        wcwords.enter()
            .append("text")
            .style("opacity", 0);
        wcwords.exit().transition().call(step, 1, "out").remove()
            .call(log, "exit");
    
        // modify the selection by rebinding the original data
        // but with an extra element concatenated
        // and return the update selection
        var wcwords = wcwords.data(words.concat(["No"]))
            .call(log, "wcwords.data(words.concat(['No'])) update");
    
        // update the DOM and merge the exit selection into the update selection
        wcwords.enter().append('text')
            .attr("transform", function(d,i) {
                return "translate(" + [5, 20*i] + ")";
            })
            .text(function(d) { return d; })
            .style("opacity", 0)
            .call(step, 2, "in")
            .call(log, "enter");
    
        function datum(n){
            return n ? d3.select(n).datum() : "";
        }
        function step (selection, s, type) {
            var id = Date.now(),
                opacity = {in: 1, out: 0},
                t = 1000,
                w = 0, b = "";
            selection.each(function(d){w = Math.max(w, d.length) });
            b = new Array(w+4).join('_')
            this.transition(Date.now()).delay(s * t).duration(t)
                .each("start." + id, function(d, i, j){
                    var n = this, node = d3.select(n),
                        DOM_node = d3.select(selection[0].parentNode)
                        .selectAll(this.nodeName).filter(function(d){return node.datum() === d});
                    DOM_node = DOM_node.length ? DOM_node[0][0] : null;
                    transitionLog.writeLine(["start ", (""+id).slice(-4), s, type, (d+b).slice(0,w), style(this, "opacity") || "null", DOM_node === n].join("\t"))
                })
                .each("interrupt." + id, function(d){
                    console.log(["\tinterrupt ", id, type, style(this, "opacity"), s].join("\t"))
                })
                .each("end." + id, function(d){
                    var n = this, node = d3.select(n),
                        DOM_node = d3.select(selection[0].parentNode)
                            .selectAll(this.nodeName).filter(function(d){return node.datum() === d});
                    DOM_node = DOM_node.length ? DOM_node[0][0] : null;
                    transitionLog.writeLine(["end", (""+id).slice(-4), s, type, (d+b).slice(0,w), style(this, "opacity") || "null", DOM_node === n].join("\t"))
                })
                .style("opacity", opacity[type]);
            function style(n, a){return d3.select(n).style(a)}
        }
    
        function log(selection, title){
            outputLog.writeLine(title);
            outputLog.writeLine(this[0].map(datum), 1);
        }
    
        function OutputLog(selector) {
            var outputLog = d3.select(selector)
                .style({
                    "display"    : "inline-block",
                    "font-size"  : "10px",
                    "margin-left": "10px",
                    padding      : "1em",
                    "white-space": "pre",
                    "background" : "#fd9801",
                });
            outputLog.writeLine = (function() {
                var s = "";
                return function(l, indent) {
                    this.text((s += ((indent ? "  " : "") + l + "\n")));
                }
            })();
            return outputLog
        }
    &#13;
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.6/d3.min.js" charset="UTF-8"></script>
    <svg id='wordcloud'></svg>
    <div id="output-log"></div>
    <div id="transition-log"></div>
    &#13;
    &#13;
    &#13;