我有多个从CorelDraw导出的现有SVG圆,希望每个圆都在悬停时的工具提示中显示唯一的文本。 在g元素里面是圆圈的地方,我添加了文本元素。我将每个文本放在相应的圆旁边并带有相应的文本。
<g id="cities" class="gradici">
<circle class="first" r="7" />
<circle class="second" r="7 />
</g>
var Citytooltip = svg.selectAll("g.gradici").selectAll("text")
.data(naziviGradova)
.enter()
.append("text")
.style("visibility", "hidden")
.attr("x", function(d,i){return graddx[i]})
.attr("y",function(d,i){return graddy[i]})
.text(function(d) {return d;})
.attr("font-size", "10px")
.attr("fill", "#black");
当我将鼠标悬停在所有圆圈旁边的所有圆圈都可见/隐藏的任何圆圈上时,我就会想到。
var city= svg.selectAll("#cities circle");
city
.on("mouseover", (function(){Citytooltip.style("visibility",
"visible");}))
.on("mouseout", (function(){Citytooltip.style("visibility",
"hidden");}));
但是我正在徘徊如何使文本在我所徘徊的圆圈上方可见/隐藏。我想我应该以某种方式迭代低谷城市,但是我被困在如何做到这一点上。有什么想法吗?
答案 0 :(得分:0)
添加title
,description
或metadata
元素作为circle
元素的内容,以便用户代理提供工具提示(取决于用户代理):
<g id="cities" class="gradici">
<desc>A group of circles</desc>
<circle class="first" r="7">
<desc>First circle</desc>
</circle>
<circle class="second" r="7>
<desc>Second circle</desc>
</circle>
</g>
对于现代台式机和移动Web浏览器,所提供的元素描述通常按照您的描述和期望呈现-当用户将鼠标指针悬停在circle
元素上(例如,包含desc
元素。
我的建议是,不要使用复杂的基于脚本的解决方案来重塑轮子,这些解决方案始终会给您的某些用户带来折磨的风险,而不是像上述内容已经成为SVG的一部分并足以满足您的需要。 / p>
答案 1 :(得分:0)
如果用户代理工具提示不切实际,则必须自己实现某些功能。我决定仍然依靠声明性的desc
元素,即使我们自己使用呈现工具提示,也可以轻松使用它们。
在下面的SVG文档中,工具提示定义用作模板,每当“鼠标”指针(实际上可以产生“ mouse *”事件的任何东西)进入元素时,我们都会提取文档片段({ 1}}),即其Range
元素的内容,并将这些内容复制到工具提示的“内容”组/图形中。最重要的是,我们计算应该显示工具提示的位置-在鼠标指针的顶端-然后调整背景“面板”的大小,以使其实际上类似于大多数人接受的工具提示。
您可以添加自己的样式甚至动画来进一步完善所需的结果。
更多解释在下面的代码注释中:
desc
如果没有超时触发等代码,代码会更简单,但是经过深思熟虑,用户友好的工具提示实现将使用延迟并补偿杂散指针的移动,因此,我认为将一些骨骼框架保留在其中是很有意义的这些位置,并演示如何实施它们。
从某种意义上来说,这是相当理想的,因为您每次只使用一组侦听器-无需将侦听器分配给要跟踪的每个元素。如果元素具有描述,则此脚本将确保显示所有工具提示。暂时,我们确实将<?xml version="2.0" encoding="utf-8" ?>
<!DOCTYPE svg SYSTEM "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 100 100">
<style>
#rich-tooltip {
pointer-events: none;
}
#rich-tooltip .panel {
fill: silver;
}
</style>
<defs>
<!-- the actual template, will be removed from the context and always shown as appended at the end of the document body, so that it is rendered above everything else. -->
<g id="rich-tooltip">
<rect class="panel" /><!-- just some decorative background -->
<g class="contents" /><!-- contents of an element's description will be added as child nodes of this element -->
</g>
</defs>
<circle cx="50" cy="50" r="25" fill="yellow">
<desc><circle cx="10" cy="10" r="5" /><text dominant-baseline="hanging" fill="red">First circle</text></desc>
</circle>
<circle cx="70" cy="50" r="40" fill="green">
<desc><circle cx="10" cy="10" r="5" /><text dominant-baseline="hanging" fill="red">Second circle</text></desc>
</circle>
<script>
const tooltip = document.getElementById("rich-tooltip");
tooltip.remove(); /// Initial state of the tooltip is "not shown" (removed from DOM tree)
var timeout; /// We only show the tooltip once the pointer settles and some time passes
const tooltip_delay = 1000; /// Delay before showing the tooltip once pointer settles
var last_tooltip_ev; /// Auxilary variable to be able to calculate movement after showing the tooltip, so we don't remove it immediately but only once the pointer actually moved substantially, this is just a nice touch, not otherwise crucial
const remove_tooltip_move_threshold = 10; /// How many user units (pixels, normall) the cursor may move before tooltip is hidden
function on_mouse_move_event(ev) {
if(document.contains(tooltip)) { /// Is the tooltip being shown?
if(last_tooltip_ev) {
if(((x, y) => Math.sqrt(x * x + y * y))(ev.clientX - last_tooltip_ev.clientX, ev.clientY - last_tooltip_ev.clientY) >= remove_tooltip_move_threshold) { /// has the pointer moved far enough from where the tooltip was originally shown?
tooltip.remove(); /// Hide the tooltip
}
}
} else {
if(timeout) clearTimeout(timeout);
timeout = setTimeout(show_tooltip, tooltip_delay, ev);
}
}
function show_tooltip(ev) {
const desc = ev.target.querySelector(":scope > desc");
if(!desc) { /// Does the element that is under the pointer even have a description?
tooltip.remove(); /// Hide the tooltip (ignoring the fact it may not be showing in the first place)
return;
}
document.documentElement.appendChild(tooltip);
const desc_range = document.createRange();
desc_range.selectNodeContents(desc); /// Select all children of the description element, as `desc_range`
const contents = tooltip.querySelector(".contents");
const contents_range = document.createRange();
contents_range.selectNodeContents(contents); /// Select all children of the tooltip contents element, as `contents_range`
contents_range.extractContents(); /// Empty tooltip contents
contents.appendChild(desc_range.cloneContents()); /// Fill contents with previously selected description. We _copy_ the description -- the original should legitimately stay where it was
const panel = tooltip.querySelector("rect.panel");
panel.setAttribute("width", contents.getBBox().width);
panel.setAttribute("height", contents.getBBox().height); /// "Stretch" the panel so that it covers the tooltip contents
const pt = document.documentElement.createSVGPoint();
pt.x = ev.clientX;
pt.y = ev.clientY;
const view_pt = pt.matrixTransform(document.documentElement.getScreenCTM().inverse()); /// We need to convert mouse pointer coordinates to the SVG viewbox coordinates
tooltip.setAttribute("transform", `translate(${view_pt.x} ${view_pt.y})`); /// Move the tooltip to appropriate position
last_tooltip_ev = ev; /// Save the event to be able to calculate distance later (above)
}
addEventListener("mouseover", function(ev) { /// When the pointer gets over an element...
ev.target.addEventListener("mousemove", on_mouse_move_event); /// We will be tracking pointer movements to trigger timeout for showing the tooltip
ev.target.addEventListener("mouseout", function() { /// Once the pointer gets anywhere but the element itself -- like over its children or other elements...
ev.target.removeEventListener("mouseout", on_mouse_move_event); /// Cancel the whole mousemove business, the behavior will be setup by whatever element the mouse pointer gets over next anyway
}, { once: true }); /// Once, for this element, everything else will be setup by another call for "mouseover" listener
});
</script>
</svg>
侦听器分配给一个元素,但通常在任何时间点仅将一个侦听器分配给一个元素-指针离开某个元素后,侦听器就会被删除(以及其他内容)否则,它会重新分配它的另一个实例,但这很好。)
答案 2 :(得分:0)
我想解决问题,所以如果有人像我一样被卡住,我会留下答案。
var titleCreate = svg.selectAll("g.gradici circle").append("title").text("tooltip");
for (var i =0; i<naziviGradova.length; i++){
var textNazivaGradova = naziviGradova[i];
var title = svg.getElementsByTagName("title");
title[i].innerHTML = textNazivaGradova;
}