我有一个稳定的d3.js图表,大部分都运行良好。有4种节点类型:
Agent,Customer,Phone,ID_Card
有复选框可以处理节点和链接的不透明度。如果选中代理和客户,则两个节点及其之间的所有可能链接都是不透明的。图表的其余部分有
opacity(0)
此外,还有一个搜索功能,可让用户使用以下节点属性搜索图表
d.id
输入ID匹配的节点将保持不透明状态,图表的其余部分将淡出几秒钟
以下是 HTML
<label>
<input type="checkbox" name="checkb" value="Agent"> type=Agent</label>
<br />
<label>
<input type="checkbox" name="checkb" value="Customer"> type=Customer</label>
<br />
<label>
<input type="checkbox" name="checkb" value="Phone"> type=Phone</label>
<br />
<label>
<input type="checkbox" name="checkb" value="ID_Card"> type=ID_Card</label>
<br />
<input type="text" id="node" />
<button id="search">
Search_Node
</button>
</div>
以下是 javascript 代码,可确保所有过滤器协同工作。
例如,如果选中客户和电话,并且搜索功能应用于搜索客户或电话,剩下的两个未经检查的节点仍将保留在
opacity(0)
搜索将仅应用于子图。
d3.selectAll("input[name=checkb]").on("change", function() {
function getCheckedBoxes(chkboxName) {
var checkboxes = document.getElementsByName(chkboxName);
var checkboxesChecked = [];
for (var i = 0; i < checkboxes.length; i++) {
if (checkboxes[i].checked) {
checkboxesChecked.push(checkboxes[i].defaultValue);
}
}
return checkboxesChecked.length > 0 ? checkboxesChecked : " ";
}
var checkedBoxes = getCheckedBoxes("checkb");
node.style("opacity", 1);
link.style("opacity", 1);
node.filter(function(d) {
return checkedBoxes.indexOf(d.type) === -1;
})
.style("opacity", "0");
link.filter(function(d) {
return checkedBoxes.indexOf(d.source.type) === -1 ||
checkedBoxes.indexOf(d.target.type) === -1;
})
.style("opacity", "0");
link.filter(function(d) {
return checkedBoxes.indexOf(d.source.type) > -1 &&
checkedBoxes.indexOf(d.target.type) > -1;
})
.style("opacity", "1");
});
// get the checkboxes
var typeAgentChk = document.querySelectorAll('input[value="Agent"]')[0];
var typeCustomerChk = document.querySelectorAll('input[value="Customer"]')[0];
var typePhoneChk = document.querySelectorAll('input[value="Phone"]')[0];
var typeIDCardChk = document.querySelectorAll('input[value="ID_Card"]')[0];
var checkBoxes = [typeAgentChk, typeCustomerChk, typePhoneChk, typeIDCardChk];
var nodeTypes = ["Agent", "Customer", "Phone", "ID_Card"];
var filterByCheckBox = function(el) {
if (el.checked) {
// filter
filterBy("type", el.value, false);
} else {
removeFilterFor("type", el.value);
}
}
var removeFilterFor = function(attribute, value) {
var node = svg.selectAll(".node");
var link = svg.selectAll(".link");
var selected = node.filter(function(d) { // filter by attribute/value
return d[attribute] == value;
})
.style("opacity", 0)
.each(function(d) {
var index = filtered.indexOf(d.id);
if (index > -1) {
filtered.splice(index, 1);
}
});
}
var filterBy = function(attribute, value, restoreAll, highlightSelected) {
// get nodes and links - could be moved outside for efficiency
var node = svg.selectAll(".node");
var link = svg.selectAll(".link");
if (value == "none" || value == null) { // do we need this?
node.style("stroke", "white").style("stroke-width", "1");
} else {
var selected = node.filter(function(d) { // filter by attribute/value
return d[attribute] == value;
});
// get any nodes we have filtered before
var alreadyFiltered = node.filter(function(d) {
return filtered.indexOf(d.id) > -1;
})
var alreadyFilteredLinks = link.filter(function(d) {
return filtered.indexOf(d.source.id) > -1 && filtered.indexOf(d.target.id) > -1;
})
// hide all
node.style("opacity", 0);
link.style("opacity", 0);
if (highlightSelected) {
svg.selectAll(".node, .link").transition()
.duration(1000)
.style("opacity", 0);
}
selected.style("opacity", 1)
.each(function(d) { // save the id's in the filtered list
if (filtered.indexOf(d.id) == -1) {
filtered.push(d.id);
}
});
// but the already filtered and the currently selected
alreadyFiltered.transition()
.duration(3000)
.style("opacity", 1);
alreadyFilteredLinks.transition()
.duration(3000)
.style("opacity", 1);
if (restoreAll) { // restore all
filtered = [];
svg.selectAll(".node, .link").transition()
.duration(5000)
.style("opacity", 1);
}
}
}
for (let i = 0; i < checkBoxes.length; i++) {
checkBoxes[i].checked = true;
//filterByCheckBox(checkBoxes[i]); // add all ids in filtered
checkBoxes[i].addEventListener("click", function() {
filterByCheckBox(checkBoxes[i])
});
}
searchBtn.addEventListener("click", function() {
// get value to filter with
var selectedVal = document.getElementById('node').value;
if (nodeTypes.indexOf(selectedVal) > -1) { // we filter by type
filterBy("type", selectedVal, false);
} else {
console.log("filterBy", "id", selectedVal)
filterBy("id", selectedVal, false, true);
}
});
这似乎在很大程度上与预期一致。复选框的取消选中工作正常,似乎可以在
处创建未经检查的节点 opacity(0)
但是当再次检查那些节点时,它似乎效果不佳。
例如,如果客户和手机仍未检查,其他两项仍未选中,则只有客户和手机是可见的。但是,如果再次重新检查代理节点,即使 ID_Card 节点仍然未选中,也会显示该节点。
在任何节点的检查期间都是如此。即使未经检查的节点也会显示。
以下是fiddle