javascript 将链接插入 Google Charts api 数据?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/12701772/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-26 16:55:25  来源:igfitidea点击:

Insert Links into Google Charts api data?

javascriptwebgoogle-visualization

提问by Mike

I have been playing around with Google charts quite a bit over in the google charts play ground here:

我在这里的谷歌图表游乐场玩了很多谷歌图表:

Link

关联

The code I have been playing with is this:

我一直在玩的代码是这样的:

function drawVisualization() {
  // Create and populate the data table.
  var data = google.visualization.arrayToDataTable([
    ['Year', 'Austria'],
    ['2003',  1336060],
    ['2004',  1538156],
    ['2005',  1576579],
    ['2006',  1600652],
    ['2007',  1968113],
    ['2008',  1901067]
  ]);

  // Create and draw the visualization.
  new google.visualization.BarChart(document.getElementById('visualization')).
      draw(data,
           {title:"Yearly Coffee Consumption by Country",
            width:600, height:400,
            vAxis: {title: "Year"},
            hAxis: {title: "Cups"}}
      );
}

and that gives me a nice chart that looks like this:

这给了我一个漂亮的图表,看起来像这样:

enter image description here

在此处输入图片说明

I am trying to have this chart fit the needs of my website, and to do this, I need to make the bar names on the left links to another page. So for example 2003 would be a link that the user can click ans so would 2004 etc.

我试图让这个图表适合我网站的需要,为此,我需要将左侧的栏名称链接到另一个页面。因此,例如 2003 将是用户可以单击的链接,2004 等也是如此。

I tried to do something like this:

我试图做这样的事情:

function drawVisualization() {
  // Create and populate the data table.
  var data = google.visualization.arrayToDataTable([
    ['Year', 'Austria'],
    ['<a href="url">Link text</a>',  1336060],
    ['2004',  1538156],
    ['2005',  1576579],
    ['2006',  1600652],
    ['2007',  1968113],
    ['2008',  1901067]
  ]);

  // Create and draw the visualization.
  new google.visualization.BarChart(document.getElementById('visualization')).
      draw(data,
           {title:"Yearly Coffee Consumption by Country",
            width:600, height:400,
            vAxis: {title: "Year"},
            hAxis: {title: "Cups"}}
      );
}

But I could only hope for it to be that easy and it wasn't. Does anyone know if this is at all possible?

但我只能希望它那么容易,但事实并非如此。有谁知道这是否可能?

采纳答案by manzoid

This is non-trivial because the output you are seeing is SVG, not HTML. Those labels in your example ("2004", "2005", etc) are embedded inside SVG text nodes, so inserting raw HTML markup inside them will not be rendered as HTML.

这很重要,因为您看到的输出是 SVG,而不是 HTML。您示例中的那些标签(“2004”、“2005”等)嵌入在 SVG 文本节点中,因此在其中插入原始 HTML 标记不会呈现为 HTML。

The workaround is to scan for the text nodes containing the target values (again, "2004", "2005" etc) and replace them with ForeignObjectelements. ForeignObjectelements can contain regular HTML. These then need to be positioned more-or-less where the original SVG text nodes had been.

解决方法是扫描包含目标值(同样是“2004”、“2005”等)的文本节点并将它们替换为ForeignObject元素。 ForeignObject元素可以包含常规 HTML。然后需要将这些或多或少定位在原始 SVG 文本节点所在的位置。

Here is a sample snippet illustrating all this. It is tuned to your specific example, so when you switch to rendering whatever your real data is, you will want to modify and generalize this snippet accordingly.

这是说明所有这些的示例片段。它针对您的特定示例进行了调整,因此当您切换到渲染任何真实数据时,您将需要相应地修改和概括此代码段。

// Note: You will probably need to tweak these deltas
// for your labels to position nicely.
var xDelta = 35;
var yDelta = 13;
var years = ['2003','2004','2005','2006','2007','2008'];
$('text').each(function(i, el) {
  if (years.indexOf(el.textContent) != -1) {
    var g = el.parentNode;
    var x = el.getAttribute('x');
    var y = el.getAttribute('y');
    var width = el.getAttribute('width') || 50;
    var height = el.getAttribute('height') || 15;

    // A "ForeignObject" tag is how you can inject HTML into an SVG document.
    var fo = document.createElementNS("http://www.w3.org/2000/svg", "foreignObject")
    fo.setAttribute('x', x - xDelta);
    fo.setAttribute('y', y - yDelta);
    fo.setAttribute('height', height);
    fo.setAttribute('width', width);
    var body = document.createElementNS("http://www.w3.org/1999/xhtml", "BODY");
    var a = document.createElement("A");
    a.href = "http://yahoo.com";
    a.setAttribute("style", "color:blue;");
    a.innerHTML = el.textContent;
    body.appendChild(a);
    fo.appendChild(body);

    // Remove the original SVG text and replace it with the HTML.
    g.removeChild(el);
    g.appendChild(fo);
  }
});

Minor note, there is a bit of jQuery in there for convenience but you can replace $('text')with document.getElementsByTagName("svg")[0].getElementsByTagName("text").

小注意,为了方便起见,里面有一些 jQuery,但你可以 $('text')document.getElementsByTagName("svg")[0].getElementsByTagName("text").

回答by Mark Butler

Manzoid's answer is good, but "some assembly is still required" as it just displays an alert box rather than following the link. Here is a more complete answer BUT it makes the bars clickable rather than the labels. I create a DataTablethat includes the links then create a DataViewfrom that to select the columns I want to display. Then when the select event occurs, I just retrieve the link from the original DataTable.

Manzoid 的回答很好,但“仍然需要一些组装”,因为它只显示一个警告框而不是点击链接。这是一个更完整的答案,但它使条形图可点击而不是标签。我创建了一个包含链接的DataTable,然后从中创建一个DataView以选择我想要显示的列。然后,当发生选择事件时,我只需从原始DataTable中检索链接。

<html>
  <head>
    <script type="text/javascript" src="https://www.google.com/jsapi"></script>
    <script type="text/javascript">
      google.load("visualization", "1", {packages:["corechart"]});
      google.setOnLoadCallback(drawChart);
      function drawChart() {
        var data = google.visualization.arrayToDataTable([
          ['Year', 'link', 'Austria'],
          ['2003', 'http://en.wikipedia.org/wiki/2003',  1336060],
          ['2004', 'http://en.wikipedia.org/wiki/2004', 1538156],
          ['2005', 'http://en.wikipedia.org/wiki/2005', 1576579],
          ['2006', 'http://en.wikipedia.org/wiki/2006', 1600652],
          ['2007', 'http://en.wikipedia.org/wiki/2007', 1968113],
          ['2008', 'http://en.wikipedia.org/wiki/2008', 1901067]             
        ]);
       var view = new google.visualization.DataView(data);
       view.setColumns([0, 2]);

       var options = {title:"Yearly Coffee Consumption by Country",
            width:600, height:400,
            vAxis: {title: "Year"},
            hAxis: {title: "Cups"}};

       var chart = new google.visualization.BarChart( 
           document.getElementById('chart_div'));
       chart.draw(view, options);

       var selectHandler = function(e) {
          window.location = data.getValue(chart.getSelection()[0]['row'], 1 );
       }

       google.visualization.events.addListener(chart, 'select', selectHandler);
      }
    </script>
  </head>
  <body>
    <div id="chart_div" style="width: 900px; height: 900px;"></div>
  </body>
</html>

回答by manzoid

Since the SVG-embedding route is (understandably) too hairy for you to want to muck with, let's try a completely different approach. Assuming that you have the flexibility to alter your functional specification a bit, such that the barsare clickable, not the labels, then here's a much simpler solution that will work.

由于 SVG 嵌入路线(可以理解)对您来说太麻烦了,让我们尝试一种完全不同的方法。假设您可以灵活地稍微更改您的功能规范,例如是可点击的,而不是标签,那么这里有一个更简单的解决方案。

Look for the alertin this snippet, that's the part that you will customize to do the redirect.

alert在此代码段中查找,这是您将自定义以执行重定向的部分。

function drawVisualization() {
  // Create and populate the data table.
  var rawData = [
    ['Year', 'Austria'],
    ['2003',  1336060],
    ['2004',  1538156],
    ['2005',  1576579],
    ['2006',  1600652],
    ['2007',  1968113],
    ['2008',  1901067]
  ];
  var data = google.visualization.arrayToDataTable(rawData);

  // Create and draw the visualization.
  var chart = new google.visualization.BarChart(document.getElementById('visualization'));
  chart.draw(data,
           {title:"Yearly Coffee Consumption by Country",
            width:600, height:400,
            vAxis: {title: "Year"},
            hAxis: {title: "Cups"}}
      );
  var handler = function(e) {
    var sel = chart.getSelection();
    sel = sel[0];
    if (sel && sel['row'] && sel['column']) {
      var year = rawData[sel['row'] + 1][0];
      alert(year); // This where you'd construct the URL for this row, and redirect to it.
    }
  }
  google.visualization.events.addListener(chart, 'select', handler);
}

回答by overflowing

I apparently don't have enough reputation points to comment directly to a previous reply, so my apologies for doing so as a new post. manzoid's suggestion is great but has one issue I found, and it looks like Mark Butler might have run into the same problem (or unknowingly sidestepped it).

我显然没有足够的声望点直接对之前的回复发表评论,所以我很抱歉作为一个新帖子这样做。manzoid 的建议很好,但我发现了一个问题,看起来 Mark Butler 可能遇到了同样的问题(或者在不知不觉中回避了它)。

if (sel && sel['row'] && sel['column']) {

That line keeps the first data point from being clickable. I used it on a Jan-Dec bar chart, and only Feb-Dec were clickable. Removing sel['row'] from the condition allows Jan to work. I don't know that the if() condition is really even necessary, though.

该行使第一个数据点无法点击。我在 1 月至 12 月的条形图上使用它,只有 2 月至 12 月可点击。从条件中删除 sel['row'] 允许 Jan 工作。不过,我不知道 if() 条件是否真的是必要的。

回答by Rafal Enden

You should use formatters.

你应该使用格式化程序

If you replace value with HTML then sorting will not work properly.

如果您用 HTML 替换值,则排序将无法正常工作。

回答by tokkonopapa

Here's another solution that wraps each text tag for label with anchor tag.

这是另一种解决方案,它用锚标签包装标签的每个文本标签。

  • no ForeignObject
  • clickable label
  • stylable by css (hover effect)
  • ForeignObject
  • 可点击标签
  • 可通过 css 设置样式(悬停效果)

Here's a sample: https://jsfiddle.net/tokkonoPapa/h3eq9m9p/

这是一个示例:https: //jsfiddle.net/tokkonoPapa/h3eq9m9p/

/* find the value in array */
function inArray(val, arr) {
    var i, n = arr.length;
    val = val.replace('…', ''); // remove ellipsis
    for (i = 0; i < n; ++i) {
        if (i in arr && 0 === arr[i].label.indexOf(val)) {
            return i;
        }
    }
    return -1;
}

/* add a link to each label */
function addLink(data, id) {
    var n, p, info = [], ns = 'hxxp://www.w3.org/1999/xlink';

    // make an array for label and link.
    n = data.getNumberOfRows();
    for (i = 0; i < n; ++i) {
        info.push({
            label: data.getValue(i, 0),
            link:  data.getValue(i, 2)
        });
    }

    $('#' + id).find('text').each(function(i, elm) {
        p = elm.parentNode;
        if ('g' === p.tagName.toLowerCase()) {
            i = inArray(elm.textContent, info);
            if (-1 !== i) {
                /* wrap text tag with anchor tag */
                n = document.createElementNS('hxxp://www.w3.org/2000/svg', 'a');
                n.setAttributeNS(ns, 'xlink:href', info[i].link);
                n.setAttributeNS(ns, 'title', info[i].label);
                n.setAttribute('target', '_blank');
                n.setAttribute('class', 'city-name');
                n.appendChild(p.removeChild(elm));
                p.appendChild(n);
                info.splice(i, 1); // for speeding up
            }
        }
    });
}

function drawBasic() {
    var data = google.visualization.arrayToDataTable([
        ['City', '2010 Population', {role: 'link'}],
        ['New York City, NY', 8175000, 'hxxp://google.com/'],
        ['Los Angeles, CA',   3792000, 'hxxp://yahoo.com/' ],
        ['Chicago, IL',       2695000, 'hxxp://bing.com/'  ],
        ['Houston, TX',       2099000, 'hxxp://example.com'],
        ['Philadelphia, PA',  1526000, 'hxxp://example.com']
    ]);

    var options = {...};
    var chart = new google.visualization.BarChart(
        document.getElementById('chart_div')
    );

    chart.draw(data, options);

    addLink(data, 'chart_div');
}