【问题标题】:Rendered SVG image in browser: copy element names to clipboard在浏览器中渲染 SVG 图像:将元素名称复制到剪贴板
【发布时间】:2019-11-13 00:58:10
【问题描述】:

在这个网页上:http://viz-js.com/ 我们看到一个从文本文件中呈现的图表。

如果您将鼠标悬停在其中一个图形元素上,其标签会出现在弹出窗口中。 (本例中为“开始”)

问题:有没有办法让标签可选择或添加一些 JavaScript 以允许将弹出窗口的文本复制到剪贴板?

我对此的实现有很长的节点名称(最多 44 个字符),我希望能够以某种方式进行复制。

谢谢。

编辑:到目前为止尝试的操作。

使用 Chrome 的“检查”选项,我看到 SVG 中的节点似乎具有“节点”的类名,所以我尝试了以下 JavaScript,但没有效果:

$('.big').hover(function () {
    // will not work, no user action
  $('input').select();
    document.execCommand('copy');
});

$('.big').mousedown(function () {
    //works
  document.execCommand('copy');
});

而且我似乎无法使用任何 CSS 样式来影响图表的外观。

【问题讨论】:

    标签: javascript svg graphviz


    【解决方案1】:

    查看 SVG,您可以看到悬停卡片的文本来自每个形状各自组的 <title> DOM 元素。您可以通过编辑 DOM 并修改其中一个标题来判断:当您将鼠标悬停在形状上时,您会看到一个新文本。

    所以,我们只需要从那里获取文本和send it to the clipboard

    编辑:现在应该更容易运行了。它只需要等到 SVG 的 g.graph 元素加载到页面,而不是每次渲染时。

    (function addListener() {
      // This time, add the listener to the graph itself
      document.querySelector('.graph').addEventListener('click', event => {
        let str = ""
        // Grab all the siblings of the element that was actually clicked on
        for (const sibling of event.target.parentElement.children) {
          // Check if they're the title
          if (sibling.nodeName != 'title') continue;
          str = sibling.innerHTML;
          break;
        }
    
        const ta = document.createElement('textarea');
        ta.value = str;
        ta.setAttribute('readonly', '');
        ta.style = { position: 'absolute', left: '-9999px' };
        document.body.appendChild(ta);
        ta.select();
        document.execCommand('copy');
        document.body.removeChild(ta);
    
        if (str == "") console.log('Nothing found to copy!');
        else console.log(`"${str}" copied to clipboard!`);
      });
    })();
    

    如果您想将其放在页面的源代码中,而不是粘贴到 Chrome 控制台,那么请去掉函数声明并将其从括号中取出。它会在它所在的文件被加载时运行。


    原解决方案:

    // Function wrapped in brackets and called immediately after declaration
    // (so that it can be run from the Chrome console):
    (function addListeners() {
      // Grab every SVG 'group' in the 'graph' group:
      for (const el of document.querySelectorAll('.graph g')) {
        // Tell each group to listen for a click on itself:
        el.addEventListener('click', event => {
          // Create an empty string variable to store the title in
          let str = "";
          // Loop through all the elements in the group for one called 'title'
          for (const child of el.children) {
            if (child.nodeName != 'title') continue;
            // Store that title
            str = child.innerHTML;
          }
    
          // Copy the string to the clipboard (see link above)
          const ta = document.createElement('textarea');
          ta.value = str;
          ta.setAttribute('readonly', '');
          ta.style = { position: 'absolute', left: '-9999px' };
          document.body.appendChild(ta);
          ta.select();
          document.execCommand('copy');
          document.body.removeChild(ta);
    
          console.log(`"${str}" copied to clipboard!`);
        });
      }
    })();
    

    我在您链接的页面的 Chrome 开发控制台中对此进行了测试,效果很好。

    【讨论】:

    • 谢谢马修。我可能要等到星期一回来工作时才能测试这个。
    • 我无法让它工作 - 我将您的代码添加到我自己页面的
    • 我将该代码块复制到 Chrome 开发人员工具的控制台选项卡中。似乎他们的网站使用了一些时髦的导入,这使得在本地保存变得困难。请注意,这是通过向 SVG 中的所有组添加侦听器来实现的——这意味着如果您在 SVG 组出现之前运行此函数,它们将不会添加侦听器。让我看看是否可以将其修改为图形元素上的单个事件侦听器...我还在我的代码中添加了一些 cmets。
    • 好主意。然而,这会产生很多textareas。也许创建一个隐藏在 html 上的输入类型并在点击时重置它的值会是一个更好的选择
    • @enxaneta 是真的。我认为简单地将ta 的声明移到事件侦听器之外,确保只创建一个,这可能是最快的解决方法。
    猜你喜欢
    • 2012-08-28
    • 1970-01-01
    • 2011-01-20
    • 2021-01-14
    • 2012-11-30
    • 2023-03-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多