d3 Не правильно отменять выбор узлов

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

  1. Если вы выберете узел A, затем выберите узел B (в этот момент они оба желтого цвета, как и должно быть), затем вернитесь назад и дважды щелкните узел A, чтобы отменить выбор, и он снова будет считан выбранным (остается желтым).
  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 выполняет фактическую проверку свойства и меняет цвет.

Будем благодарны за любую помощь в том, почему происходит такое странное поведение!

1 ответ

Решение

Если вы выберете A, то selected_node устанавливается в А.

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

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

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

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

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

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

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

Другие вопросы по тегам