SVG不能作为骨架视图正确渲染

21

我正在使用d3.js在svg中渲染世界地图(使用https://github.com/johan/world.geo.json/blob/master/countries.geo.json作为特征)。我将渲染逻辑封装在Backbone View中。当我渲染视图并将其附加到DOM时,浏览器中没有显示任何内容,尽管在查看生成的HTML时正确生成了SVG标记。如果不在Backbone.View中封装,则可以正常呈现。这是我的代码使用Backbone.view:

/**
 * SVG Map view
 */
var MapView = Backbone.View.extend({
    tagName: 'svg',
    translationOffset: [480, 500],
    zoomLevel: 1000,

    /**
     * Sets up the map projector and svg path generator
     */
    initialize: function() {
        this.projector = d3.geo.mercator();
        this.path = d3.geo.path().projection(this.projector);
        this.projector.translate(this.translationOffset);
        this.projector.scale(this.zoomLevel);
    },

    /**
     * Renders the map using the supplied features collection
     */
    render: function() {
        d3.select(this.el)
          .selectAll('path')
          .data(this.options.featureCollection.features)
          .enter().append('path')
          .attr('d', this.path);
    },

    /**
     * Updates the zoom level
     */
    zoom: function(level) {
        this.projector.scale(this.zoomLevel = level);
    },

    /**
     * Updates the translation offset
     */
    pan: function(x, y) {
        this.projector.translate([
            this.translationOffset[0] += x,
            this.translationOffset[1] += y
        ]);
    },

    /**
     * Refreshes the map
     */
    refresh: function() {
        d3.select(this.el)
          .selectAll('path')
          .attr('d', this.path);
    }
});

var map = new MapView({featureCollection: countryFeatureCollection});
map.$el.appendTo('body');
map.render();

这里是代码,可以正常工作,而不使用Backbone.View。
var projector = d3.geo.mercator(),
    path = d3.geo.path().projection(projector),
    countries = d3.select('body').append('svg'),
    zoomLevel = 1000;

coords = [480, 500];
projector.translate(coords);
projector.scale(zoomLevel);

countries.selectAll('path')
         .data(countryFeatureCollection.features)
         .enter().append('path')
         .attr('d', path);

我还附上了生成的SVG标记的截图。你有什么想法,可能出了什么问题吗?

enter image description here

编辑 - 这是重写的make方法,解决了这个问题,如请求所示:

/**
 * Custom make method needed as backbone does not support creation of
 * namespaced HTML elements.
 */
make: function(tagName, attributes, content) {
    var el = document.createElementNS('http://www.w3.org/2000/svg', tagName);
    if (attributes) $(el).attr(attributes);
    if (content) $(el).html(content);
    return el;
}

现在,既然make方法已被移除,那么正确的做法是什么? - Rick
3个回答

27
问题出在"svg"元素需要一个命名空间。D3会自动为您添加命名空间;当您附加一个"svg"元素时,它使用命名空间"http://www.w3.org/2000/svg"。详情请参见src/core/ns.js。遗憾的是,Backbone似乎不支持带命名空间的元素。您需要更改view.make方法。然后您需要在视图上设置namespaceURI属性来设置适当的命名空间,或者只是为了与HTML5解析器一致而自动设置SVG元素的命名空间。
无论如何,解决您问题的简单方法是将SVG封装在DIV元素中,然后使用D3创建SVG元素。

1
谢谢!我在我的视图中覆盖了“make”,并使用了createElementNS和“http://www.w3.org/2000/svg”代替createElement,这似乎解决了问题。感谢您的指导。 - rr.
1
@rr:你能把你的make方法发在某个地方吗? - Pierre Spring
1
@PierreSpring:已完成,不确定这与新版本的Backbone有多大关联,我一直没有跟进。 - rr.
1
依然相关,刚好遇到了。感谢 @mbostock,让我免于因为一脸困惑而僵住 :) - Milimetric
4
@Milimetric Make已于Backbone 0.9.10中移除。查看http://backbonejs.org/#changelog 0.9.10。更具体地,请参考https://github.com/documentcloud/backbone/pull/2041的讨论和提交请求https://github.com/documentcloud/backbone/commit/c1e62cda999dd902060ea04a11b81376aba63a16以及https://github.com/documentcloud/backbone/commit/8a42d536d8c862d259ca4d7909026798c046c3e9。 - Thaddeus Albers

4
您可以在 initialize 函数中简单地设置视图元素,如下所示:
Backbone.View.extend({
    // This is only for informaiton. The node will
    // be raplaced in the initialize function.
    tagName: 'svg',

    initialize: function () {
        this.setElement(
            d3.select($('<div/>')[0]).append('svg')[0]
        );
    }
);

这样做的好处在于更加明确易懂。

1
这是 Backbone 作者在此处提到的内容:https://github.com/documentcloud/backbone/pull/1357 - Thomas Ahle
1
你不需要创建一个div,setElement(document.createElementNS('http://www.w3.org/2000/svg','svg'))就可以了。 - Thomas Ahle

3

看这个 http://jsfiddle.net/nocircleno/QsEp2/ 来自于 http://nocircleno.com/blog/svg-with-backbone-js/

Backbone.View.extend({
  nameSpace: "http://www.w3.org/2000/svg",
  _ensureElement: function() {
     if (!this.el) {
        var attrs = _.extend({}, _.result(this, 'attributes'));
        if (this.id) attrs.id = _.result(this, 'id');
        if (this.className) attrs['class'] = _.result(this, 'className');
        var $el = $(window.document.createElementNS(_.result(this, 'nameSpace'), _.result(this, 'tagName'))).attr(attrs);
        this.setElement($el, false);
     } else {
        this.setElement(_.result(this, 'el'), false);
     }
 }
});

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接