jQuery Selector + SVG Incompatible?

This happens because SVG DOM spec differs a lot from HTML DOM.

SVG DOM is a different dialect, and some properties have same names but mean different things. For example, to get the className of an svg element, you use:

svg.className.baseVal

The properites affected by this are

className is SVGAnimatedString
height,width, x, y, offsetWidth, offsetHeight are SVGAnimatedLength

These Animated properties are structs, with baseVal holding the same value you’d find in HTML DOM and animatedVal holding I am not sure what.

SVG DOM is also missing some properties libraries depend on, such as innerHTML.

This breaks jQuery in many ways, anything that depends on above properties fails.

In general, SVG DOM and HTML DOM do not mix very well. They work together just enough to lure you in, and then things break quietly, and another angel loses its wings.

I wrote a little jQuery extension that wraps SVG elements to make them look more like HTML DOM

(function (jQuery){
    function svgWrapper(el) {
        this._svgEl = el;
        this.__proto__ = el;
        Object.defineProperty(this, "className", {
            get:  function(){ return this._svgEl.className.baseVal; },
            set: function(value){    this._svgEl.className.baseVal = value; }
        });
        Object.defineProperty(this, "width", {
            get:  function(){ return this._svgEl.width.baseVal.value; },
            set: function(value){    this._svgEl.width.baseVal.value = value; }
        });
        Object.defineProperty(this, "height", {
            get:  function(){ return this._svgEl.height.baseVal.value; },
            set: function(value){    this._svgEl.height.baseVal.value = value; }
        });
        Object.defineProperty(this, "x", {
            get:  function(){ return this._svgEl.x.baseVal.value; },
            set: function(value){    this._svgEl.x.baseVal.value = value; }
        });
        Object.defineProperty(this, "y", {
            get:  function(){ return this._svgEl.y.baseVal.value; },
            set: function(value){    this._svgEl.y.baseVal.value = value; }
        });
        Object.defineProperty(this, "offsetWidth", {
            get:  function(){ return this._svgEl.width.baseVal.value; },
            set: function(value){    this._svgEl.width.baseVal.value = value; }
        });
        Object.defineProperty(this, "offsetHeight", {
            get:  function(){ return this._svgEl.height.baseVal.value; },
            set: function(value){    this._svgEl.height.baseVal.value = value; }
        });
    };

    jQuery.fn.wrapSvg = function() {
        return this.map(function(i, el) {
            if (el.namespaceURI == "http://www.w3.org/2000/svg" && !('_svgEl' in el))
                return new svgWrapper(el);
            else
                return el;
            });
        };
})(window.jQuery);

It creates a wrapper around SVG objects that makes them look like HTML DOM to jQuery. I’ve used it with jQuery-UI to make my SVG elements droppable.

The lack of DOM interoperability between HTML and SVG is a total disaster. All the sweet utility libraries written for HTML have to be reinvented for SVG.

Leave a Comment