Почему selection.exit().size() равен нулю?

#javascript #d3.js

#javascript #d3.js

Вопрос:

Я пытаюсь удалить элементы rect из svg, присоединив пустой массив к выделенному d3.

Это не работает.

Предположительно, я что-то неправильно понял.

Приведенный ниже фрагмент кода настраивает svg. Когда вы нажимаете кнопку «Очистить», ничего не происходит.

У меня есть 3 вопроса (также отмеченные в комментариях к коду) ;

  1. Почему выделение не сообщает размер 4 после объединения 4 элементов данных и создания 4 связанных элементов?
  2. Почему в функции clearSVG размер exit() равен 0 после присоединения данных «no» к существующему выделению.
  3. Почему прямоугольники не удалены (вероятно, ответ дан в Q2).

 function init(){
    let initialData = [
        {x:0,y:0,c:"black"},
        {x:0,y:90,c:"orange"},
        {x:90,y:0,c:"green"},
        {x:90,y:90,c:"yellow"}        
    ];
    
    let nodes = d3.select("#svg").selectAll("dataNode") ;
            
    //console.log(`empty nodes size: ${nodes.size()}  all good .. ?`);      // reports 0  
    
    nodes    
    .data(initialData)
    .enter()
    .append(`svg:rect`)
    .attr("class", `dataNode`)
    .attr("x", function(d){return d.x;})
    .attr("y", function(d){return d.y;})     
    .attr("width", 10)
    .attr("height", 10)      
    .attr("fill", function(d){return d.c;}) ;
    
    //console.log(`initial data nodes size: ${nodes.size()} - Q1. why is this not 4 ?`); //reports 0
   
};

function clearSVG(){
    let nodes = d3.select("#svg").selectAll(".dataNode"); // all 4 rect elements in the svg
    //console.log(`nodes size: ${nodes.size()}`);// looking good.. 4 nodes reported
    let newData = [] ; // nada, no data 
    nodes.data(newData) ; // join nothing to the rects - no datum for any element in the selection
    //console.log(`exit size: ${nodes.exit().size()} - Q2. Why is this 0 and not 4 ?`);
    nodes.exit().remove();    
    //console.log(`Q3. Why are the rects still present ?`);
};


init();  
 *{
    border:none;
    padding:0;       
    font-family:Arial;
    box-sizing:border-box;
}
body{
    margin:10px;
}
#svg{
    display: inline-block ;
    width:100px;
    height:100px;

}
#plotBackGround{
    fill: #FBFBFB;  
}  
 <link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<svg id="svg" viewBox="0 0 100 100" >
   <rect id="plotBackGround" x="0" y="0" width="100" height="100"></rect>
</svg>                           
    
<BR><BR>
       
<button class="btn btn-primary" onclick="clearSVG();">Clear</button>  

Редактировать

Подводя итог сути принятого ответа; selection.data(), .enter(), .append(), .exit() и .remove() возвращают новые выборки. Они не изменяют выделение, для которого они вызываются, или возвращают ссылку на него или его части.

Завершить редактирование

Ответ №1:

Проблема

Проблема здесь довольно проста: у вас нет правильного выбора обновления.

Давайте посмотрим. Сначала вы делаете это:

 let nodes = d3.select("#svg").selectAll(".dataNode");
  

Затем вы изменяете данные:

 let newData = [];
nodes.data(newData);
  

Однако вы не меняете nodes , которые остаются такими, какими они были. Когда вы позже сделаете:

 nodes.exit().remove();
  

На самом деле вы просто делаете это:

 d3.select("#svg").selectAll(".dataNode").exit().remove();
  

Что, как вы можете видеть, имеет мало смысла.

Решение

Напишите правильный выбор обновления. Это должно быть:

 nodes = nodes.data(newData);
  

Которое, если мы подставим значение nodes , даст нам фактически это:

 d3.select("#svg").selectAll(".dataNode").data(newData).exit().remove();
  

Вот код только с этим изменением:

 function init(){
    let initialData = [
        {x:0,y:0,c:"black"},
        {x:0,y:90,c:"orange"},
        {x:90,y:0,c:"green"},
        {x:90,y:90,c:"yellow"}        
    ];
    
    let nodes = d3.select("#svg").selectAll("dataNode") ;
            
    //console.log(`empty nodes size: ${nodes.size()}  all good .. ?`);      // reports 0  
    
    nodes    
    .data(initialData)
    .enter()
    .append(`svg:rect`)
    .attr("class", `dataNode`)
    .attr("x", function(d){return d.x;})
    .attr("y", function(d){return d.y;})     
    .attr("width", 10)
    .attr("height", 10)      
    .attr("fill", function(d){return d.c;}) ;
    
    //console.log(`initial data nodes size: ${nodes.size()} - Q1. why is this not 4 ?`); //reports 0
   
};

function clearSVG(){
    let nodes = d3.select("#svg").selectAll(".dataNode"); // all 4 rect elements in the svg
    //console.log(`nodes size: ${nodes.size()}`);// looking good.. 4 nodes reported
    let newData = [] ; // nada, no data 
    nodes = nodes.data(newData) ; // join nothing to the rects - no datum for any element in the selection
    //console.log(`exit size: ${nodes.exit().size()} - Q2. Why is this 0 and not 4 ?`);
    nodes.exit().remove();    
    //console.log(`Q3. Why are the rects still present ?`);
};


init();  
 *{
    border:none;
    padding:0;       
    font-family:Arial;
    box-sizing:border-box;
}
body{
    margin:10px;
}
#svg{
    display: inline-block ;
    width:100px;
    height:100px;

}
#plotBackGround{
    fill: #FBFBFB;  
}  
 <link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<svg id="svg" viewBox="0 0 100 100" >
   <rect id="plotBackGround" x="0" y="0" width="100" height="100"></rect>
</svg>                           
    
<BR><BR>
       
<button class="btn btn-primary" onclick="clearSVG();">Clear</button>  

Комментарии:

1. это, кстати, также причина того, почему console.log выводится 0 (его первый вопрос)

2. @JanWendland Да, три вопроса в списке OP на самом деле являются всего лишь одной проблемой.

3. @Bob Да, я удалил этот пример, поскольку я не пояснил, какие методы вносят изменения на месте.