2015-05-15 2 views
0

У меня есть функция «select node» в моем силовом графике d3. По какой-то причине он не ведет себя так, как должен. Код должен сделать это, когда пользователь дважды нажимает на узел, который он выбирает/отменяет (возвращается к нормальному цвету/становится желтым). Все работает, когда вы выбираете, затем отменяете выделение узла. Вопрос приходит в следующих случаях:d3 Не правильно «Отбирать» узлы

  1. Если выбрать узел А затем выберите узел B (в этот момент они оба желтые, как это должно быть), то вернуться назад и двойной узел Клик для отмены выбора становится прочитайте как выбранный снова (остается желтым).
  2. Когда вы выбираете узел A, затем выберите узел B. Затем вы дважды щелкните узел B, чтобы отменить выбор, он будет работать по назначению. Но когда вы вернетесь к узлу двойного щелчка A (который все еще выбран), чтобы отменить выбор, он будет считан как выбранный.

Вот несколько фрагментов кода, из которых я думаю, проблема.

var edges = []; 
var nodes = []; 
var nodesHash = {}; 

function update() { 

    // clear stack of selected nodes 
    selectedNodes = []; 

    // Update link data based on edges array. 
    link = link.data(edges); 

    // Create new links 
    link.enter().append("line") 
      .attr("class", "link") 
      .style("stroke-width", 1.5); 

    // Delete removed links 
    link.exit().remove(); 

    // Update node data based on nodes array. 
    node = node.data(nodes); 

    // Create new nodes 
    node.enter().append("g") 
      .attr("class", "node") 
      .attr("id", function(d) { return d.data['id'] }) 
      .call(force.drag) 
      .on('mouseover', connectedNodes) 
      .on('mouseleave', restore) 
      .on('dblclick', highlight); 

    // Delete removed nodes 
    node.exit().remove(); 

    node.append("circle").attr("r", 11); 
    node.classed("selected", function(d) { return d === d.selected; }) 

    // Node behavior for checking if selected otherwise colors nodes to color given from JSON. 
    node.style("fill", function(d) { 
     if (d.selected === false) { 
      return d.data['color'] 
      update(); 
     } 
     else { 
      return "yellow"; 
      update(); 
     } 
    }).select("circle").style("stroke", "black"); 

    // Link color based on JSON data. 
    link.style("stroke", function(d) { return d.data['color'] }); 

    // Adds text to nodes 
    node.append("text") 
      .attr("dx", 12) 
      .attr("dy", ".35em") 
      .style("fill", "black") 
      .text(function (d) { return d.data['label']; }); 

    // Creates an index used to figure out neighbor nodes. 
    root.edges.forEach(function (d) { 
     linkedByIndex[d.data.source + "," + d.data.target] = 1; 
    }); 

    // responsive behavior for graph based on window. 
    window.addEventListener('resize', resize); 

    force.on("tick", function() { 
     link.attr("x1", function(d) { return d.source.x; }) 
       .attr("y1", function(d) { return d.source.y; }) 
       .attr("x2", function(d) { return d.target.x; }) 
       .attr("y2", function(d) { return d.target.y; }); 

     node.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; }); 
    }); 
    force.start(); 
} 

// Reset Mouse Variables 
function resetMouseVars() { 
    mousedown_node = null; 
    mouseup_node = null; 
    mousedown_link = null; 
} 

// Highlighting of selected node. 
function highlight(d) { 
    mousedown_node = d; 
    if (mousedown_node != selected_node) { 
     console.log("Selected: " + mousedown_node.data['id']); 
     selected_node = mousedown_node; 
     selected_node.selected = true; 
    } 
    else { 
     console.log("De-Selected: " + mousedown_node.data['id']); 
     selected_node = mousedown_node; 
     selected_node.selected = false; 
    } 
    resetMouseVars(); 
    update(); 
} 

function spliceLinksForNode(node) { 
    toSplice = edges.filter(
      function(e) { 
       return (e.source === node) || (e.target === node); }); 
    toSplice.map(
      function(e) { 
       edges.splice(edges.indexOf(e), 1); }); 
} 

// Delete node with prompt 
function deleteNode() { 
    console.log("Prompted to delete: " + selected_node); 
    if (confirm("Deleting selected elements will remove them from the graph entirely. Are you sure?")) { 
     if (!selected_node) alert("No node selected"); 
     if (selected_node) { 
      console.log("Deleted: " + selected_node); 
      selected_node.removed = true; 
      nodes.splice(nodes.indexOf(selected_node), 1); 
      spliceLinksForNode(selected_node); 
     } 
     selected_node = null; 
     update(); 
    } 
} 

Так что в моем коде node.on('dblclick', highlight) только изменение выбранных узлов свойства, выбран ли он или нет. node.style выполняет фактическую проверку свойства и изменение цвета.

Любая помощь по поводу того, почему происходит это странное поведение, будет оценена по достоинству!

+0

не могли бы вы дать нам jsfiddle? – Josh

+0

Мне не удалось запустить jsfiddle для запуска моего кода в прошлом. Я не знаю, почему. Я постараюсь посмотреть, смогу ли я снова это сделать. – Joey

ответ

2

Если вы выбираете, то selected_node получает значение А.

Затем, если вы выбрали B, то selected_node получает значение B

Затем, если щелкнуть еще раз то, внутри highlight(), в выражение mousedown_node != selected_node оценивает по true, потому что A, который равен mousedown_node, не равно selected_node, который по-прежнему является B, из предыдущего выбора.

Так что это ошибка.

Если вы разрешаете мультивыбор, то нет никакой возможности, чтобы одна переменная selected_node была достаточной для захвата состояния выбора. Если у вас есть selected_nodesArray, из которого вы добавляете и удаляете узлы, вы можете проверить, selected_nodes.indexOf(mousedown_node) > -1, чтобы определить, выбрано ли оно.

Но на самом деле, я не понимаю, зачем нужна эта логика вообще, если, возможно, какой-то код, который вы не включили, опирается на selected_node. На самом деле, все ваши функции подсветки должны быть только

function highlight(d) { 
    d.selected = !d.selected; 
    update(); 
} 

И это должно исправить вашу проблему.

+0

Спасибо! Причина, по которой у меня была 'selected_node' как переменная, состоит в том, что у меня есть другая функция, которая использует эту переменную, чтобы определить, какой узел удалить. Я могу видеть, как ваша функция подсветки сделает так, что несколько узлов могут иметь 'd.selected'. Мой вопрос ... без 'selected_nodes', как я могу проверить свою функцию, какой узел удалить, только исходя из' d.selected'. Я обновил свой вопрос с помощью функции удаления. – Joey

+0

Простейший способ, которым 'delete_nodes()' может получить массив только что выбранных узлов, - подмножество всех «узлов», - путем фильтрации полного списка узлов: 'selectedNodes = nodes.filter (function (d) { return d.selected;}) '. Это было бы моим рекомендуемым способом, поскольку он сохраняет функцию «highlight» так же просто, как я показал выше. Единственная хорошая причина не делать это так: если «узлы» имеют десятки тысяч элементов, и поэтому их переполнение запрещено. Для этого (маловероятного) случая см. Следующий комментарий .... – meetamit

+0

Вы можете сделать глобальный массив 'selected_nodes = []'. Затем, когда вы используете текущую функцию подсветки, вы выбираете 'selected_node = mousedown_node', чтобы изменить ее на' selected_nodes.push (mousedown_node'. Чтобы определить, выбран ли узел, вы проверяете 'selected_nodes.indexOf (someNode)> -1'. удалите выбранный узел из этого массива: 'selected_nodes.splice (selected_nodes.indexOf (selected_node), 1);' – meetamit

Смежные вопросы