如何在现有的svg圈子中添加工具提示?

时间:2019-05-15 13:01:54

标签: javascript d3.js svg

我有多个从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");}));

但是我正在徘徊如何使文本在我所徘徊的圆圈上方可见/隐藏。我想我应该以某种方式迭代低谷城市,但是我被困在如何做到这一点上。有什么想法吗?

3 个答案:

答案 0 :(得分:0)

添加titledescriptionmetadata元素作为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>

这是specified by SVG 1.1

对于现代台式机和移动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;                                    

}