SVGはバックボーンビューとして正しくレンダリングされません
-
11-12-2019 - |
質問
D3.jsを使用して、SVGで世界の地図を表示しています( https://github.com/johan/world.geo.json/blob/master/countries.geo.json 機能について)表面図でレンダリングロジックをカプセル化しています。ビューをレンダリングして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マークアップのスクリーンショットを添付しました。ここで間違っているのは何も考えていますか?
編集 - これを解決したオーバーライドされた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;
}
. 解決
The issue is that the "svg" element requires a namespace. D3 does this for your automatically; when you append an "svg" element, it uses the namespace "http://www.w3.org/2000/svg". For details, see src/core/ns.js. Backbone, unfortunately, does not appear to support namespaced elements. You'd want to change the view.make method. Then you'd need a namespaceURI property on your view to set the appropriate namespace, or just do it automatically for SVG elements for consistency with the HTML5 parser.
At any rate, a simple fix for your problem is to wrap your SVG in a DIV element, and then use D3 to create the SVG element.
他のヒント
You could simply set the view's element in the initialize function as follows:
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]
);
}
);
This has the advantage of being explicite.
Check this out http://jsfiddle.net/nocircleno/QsEp2/ from 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);
}
}
});