NVD3 图表无法在 Chrome 中计算图例文本长度,因为 Window.getComputedStyle 未正确返回字体大小

Posted

技术标签:

【中文标题】NVD3 图表无法在 Chrome 中计算图例文本长度,因为 Window.getComputedStyle 未正确返回字体大小【英文标题】:NVD3 chart fails to calculate legend text length in Chrome, since Window.getComputedStyle does not return font-size correctly 【发布时间】:2015-03-05 07:36:21 【问题描述】:

背景资料

我使用它的自定义小部件框架将 NVD3 图表集成到 Eclipse-RAP 中。图表生成为一个 div。 CSS 是通过在 javascript 中创建链接条目来动态加载的。我通过创建 SVG/文本元素来检查 CSS 是否已经加载,并检查其 font-size 是否正常(请参阅 https://***.com/a/7997710/337621)。如果加载了 CSS,我会创建图表。

问题

由于某种原因,图表在 Chrome 中无法始终正确呈现。通常在我的会话中第一次显示正确,但第二次显示总是错误的。对于错误的情况,我在控制台中找到了这个:

Error: Invalid value for <g> attribute transform="translate(NaN,5)"

如果我重新绘制图表(例如通过更新图表数据或调整大小),则图例会正确呈现。

预期:

错误的布局:

经过一些调试,我找到了相关的 d3 代码部分。 NVD3 使用此函数询问 SVG 文本元素的字体大小:

  d3_selectionPrototype.style = function(name, value, priority) 
    var n = arguments.length;
    if (n < 3) 
      if (typeof name !== "string") 
        if (n < 2) value = "";
        for (priority in name) this.each(d3_selection_style(priority, name[priority], value));
        return this;
      
      if (n < 2) return d3_window.getComputedStyle(this.node(), null).getPropertyValue(name);
      priority = "";
    
    return this.each(d3_selection_style(name, value, priority));
  ;

相关的CSS部分是这样的:

svg text 
  font: normal 12px Arial;

我在getComputedStyle 调用的行上添加了以下“打印点”(条件断点,它永远不会停止,但会打印出值)

name == 'font-size' &&
(
    console.log(this.node()) ||
    console.log( d3_window.getComputedStyle(this.node(), null) ) ||
    console.log( d3_window.getComputedStyle(this.node(), null).getPropertyValue(name) ) || 
    console.log( window.getMatchedCs-s-rules(this.node()) )
)

结果真的很奇怪。如果图表正确,我会在控制台中找到正确的布局:

这对于错误的布局:

这是错误布局的 DOM:

<svg id="ujdh846lhqubvvlg2jbh16s6q9"  >
    <g class="nvd3 nv-wrap nv-pieChart" transform="translate(20,90)">
        <g>
            <g class="nv-pieWrap">
                <g class="nvd3 nv-wrap nv-pie nv-chart-6450" transform="translate(0,0)">
                    <g>
                        <g class="nv-pie" transform="translate(928,125.5)">
                            <g class="nv-slice" fill="#1f77b4" stroke="#1f77b4">
                                <path d="M6.1477269317197136e-15,-100.4A100.4,100.4 0 0,1 65.39779726531111,76.17931551835622L0,0Z"/>
                            </g><g class="nv-slice" fill="#ff7f0e" stroke="#ff7f0e">
                                <path d="M65.39779726531111,76.17931551835622A100.4,100.4 0 0,1 -90.13957577290248,44.21557281638648L0,0Z"/>
                            </g><g class="nv-slice" fill="#2ca02c" stroke="#2ca02c">
                                <path d="M-90.13957577290248,44.21557281638648A100.4,100.4 0 0,1 -94.15031406756688,-34.869447385619964L0,0Z"/>
                            </g><g class="nv-slice" fill="#d62728" stroke="#d62728">
                                <path d="M-94.15031406756688,-34.869447385619964A100.4,100.4 0 0,1 -1.844318079515914e-14,-100.4L0,0Z"/>
                            </g>
                        </g><g class="nv-pieLabels" transform="translate(928,125.5)">
                            <g class="nv-label" transform="translate(112.95224431711586,-41.8329177051586)">
                                <rect rx="3" ry="3" style="stroke: rgb(255, 255, 255); fill: rgb(255, 255, 255);"/>
                                <text style="text-anchor: middle; fill: rgb(0, 0, 0);">alma</text>
                            </g><g class="nv-label" transform="translate(-24.246406744679096,117.98438142386297)">
                                <rect rx="3" ry="3" style="stroke: rgb(255, 255, 255); fill: rgb(255, 255, 255);"/>
                                <text style="text-anchor: middle; fill: rgb(0, 0, 0);">korte</text>
                            </g><g class="nv-label" transform="translate(-120.2954032887533,6.100692386622933)">
                                <rect rx="3" ry="3" style="stroke: rgb(255, 255, 255); fill: rgb(255, 255, 255);"/>
                                <text style="text-anchor: middle; fill: rgb(0, 0, 0);">szilva</text>
                            </g><g class="nv-label" transform="translate(-68.80925650816773,-98.86095649341644)">
                                <rect rx="3" ry="3" style="stroke: rgb(255, 255, 255); fill: rgb(255, 255, 255);"/>
                                <text style="text-anchor: middle; fill: rgb(0, 0, 0);">paradicsom</text>
                            </g>
                        </g>
                    </g>
                </g>
            </g><g class="nv-legendWrap" transform="translate(0,-90)">
                <g class="nvd3 nv-legend" transform="translate(0,5)">
                    <g transform="translate(NaN,5)">
                        <g class="nv-series" transform="translate(0,5)">
                            <circle class="nv-legend-symbol" r="5" style="stroke-width: 2px; fill: rgb(31, 119, 180); stroke: rgb(31, 119, 180);"/>
                            <text text-anchor="start" class="nv-legend-text" dy=".32em" dx="8">alma</text>
                        </g><g class="nv-series" transform="translate(0,25)">
                            <circle class="nv-legend-symbol" r="5" style="stroke-width: 2px; fill: rgb(255, 127, 14); stroke: rgb(255, 127, 14);"/>
                            <text text-anchor="start" class="nv-legend-text" dy=".32em" dx="8">korte</text>
                        </g><g class="nv-series" transform="translate(0,45)">
                            <circle class="nv-legend-symbol" r="5" style="stroke-width: 2px; fill: rgb(44, 160, 44); stroke: rgb(44, 160, 44);"/>
                            <text text-anchor="start" class="nv-legend-text" dy=".32em" dx="8">szilva</text>
                        </g><g class="nv-series" transform="translate(0,65)">
                            <circle class="nv-legend-symbol" r="5" style="stroke-width: 2px; fill: rgb(125, 0, 0); stroke: rgb(125, 0, 0);"/>
                            <text text-anchor="start" class="nv-legend-text" dy=".32em" dx="8">paradicsom</text>
                        </g>
                    </g>
                </g>
            </g>
        </g>
    </g>
</svg>

怎么可能一旦我的 SVG/文本在计算样式中没有字体大小但它始终在应用的 CSS 规则之一中具有字体大小?

Chrome 中是否存在一些已知错误?

注意,在 Firefox 中一切正常。

环境详情

Chrome 39.0.2171.71(64 位)

Kubuntu 3.13.0-29-通用

更新

我认为我受到浏览器这种“行为”的影响:How can I change the default behavior of console.log? (*Error console in safari, no add-on*)。这意味着控制台不会显示该对象在日志条目的时间点的状态,而是指当前状态。所以我在这里做了一个小实验:http://jsfiddle.net/hdv7ty6L/。我从 javascript 更改类,并检查控制台中的规则列表是否更改。它似乎是规则列表的快照。 所以还是不知道,这里有什么问题:)

测试代码:

document.body.className='redbody';
console.log(window.getMatchedCs-s-rules(document.body));
document.body.className='bluebody';
console.log("Class changed");
console.log(window.getMatchedCs-s-rules(document.body));

控制台输出:

更新 2

如果 CSS 是完全静态的而不是动态加载的,也会出现问题。

更新 3

我试图在 jsfiddle 中重现它:在 div 内动态创建 SVG,并使用异步创建的图表(单击按钮)。不幸的是,错误没有出现。 https://jsfiddle.net/ewsb4d9k/1/

【问题讨论】:

非常详细的问题:我想知道答案。由于它需要一些工作才能正确回答,我会在符合条件时考虑奖励。这似乎是一个可能的错误。 看起来您正在将图表加载到“选项卡”中,在创建或显示父选项卡和子图之间是否可能存在竞争条件? @Mark 我不这么认为。由于其他原因(等待 CSS 加载),选项卡显示已经使用 setTimeout 和回调创建了“惰性”图表。据我了解,javascript 是单线程的,因此在安排执行图表创建的异步作业之前肯定会完成标签创建。 根据我的经验,一旦你在 SVG 属性中添加了 NaN,剩下的渲染部分就没有任何意义了。您能否仅发布一小段在内部&lt;g&gt; 上设置翻译的代码?看起来你那里有竞争条件,我过去处理过类似的事情。 我想 NaN 来自legend.js (line 151/182) 中以下代码中的 x 赋值:g.attr('transform', 'translate(' + (width - margin.right - legendWidth) + ',' + margin.top + ')');。也许尝试在那里设置断点并检查 nv.d3.js 或 console.log(chart.legend.width())console.log(chart.legend.margin()) 中的这些变量 【参考方案1】:

抱歉,我对 D3 不是很流利,但我的一些想法可能会有所帮助。

您是否尝试过使用 d3.select() 方法并以这种方式单独应用字体大小,看看是否可以缩小字体/文本选择器组合的问题?也许在加载时分配一个 id 或类,然后使用静态样式表定义您的样式。

在图例文本长度中断之前,您是否注意到任何奇怪的事情?删除图例和字体 css 是否 100% 有效?

我注意到您正在使用 adblock。如果您还没有禁用它,那么值得一试。那个插件有时会做一些疯狂的事情。

您是否在加载时尝试过完整的 dom 刷新或容器刷新?这会发生什么?它是否 100% 的时间渲染?还是失败了?

$("body").html($("body").html()); 
$("#d3div").html($("#d3div").html());

如 Timo 在此线程中所示 jquery's append not working with svg element?

“它似乎确实将它们添加到 DOM 资源管理器中,但不是在屏幕上” 原因是 html 和 svg 的命名空间不同。

最简单的解决方法是“刷新”整个 svg。

看起来您并没有使用 jQuery,但在这种情况下它可能对测试有用。

很遗憾听到您的疯狂错误。希望你能找到解决办法。

【讨论】:

我不使用 JQuery。 Adblock 是有意义的。如果我使用错误的命名空间创建了 SVG 元素,则根本不会显示该元素。它已经修复,所以这不是问题。我不确定你所说的完全 dom 刷新是什么意思。我正在使用 Eclipse RAP,据我所知,它并不是一个真正的选项。使用 d3 操作字体大小是我想避免的一个选项。但真正的问题仍然存在:怎么会根据浏览器应用样式表,而样式元素却没有。就是那个问题。无论如何感谢您的回答。 虽然此答案并不能真正解决问题,但我已奖励您为帮助解决/解决问题所做的努力。 谢谢您,先生。周末愉快。

以上是关于NVD3 图表无法在 Chrome 中计算图例文本长度,因为 Window.getComputedStyle 未正确返回字体大小的主要内容,如果未能解决你的问题,请参考以下文章

单击后保持 NVD3 图例的更改位置

Nvd3.js - 向累积图表添加多个 y 轴

nvd3中一列中饼图的垂直图例

NVD3,在加载新图表之前清除 svg

带有 nvd3.js 的实时线图

在报表生成器中更改图表中的图例