Toli
Toli

Reputation: 5777

Finding Offset Position of SVG Element

I've run into this problem a lot with D3. A lot of the time I like to overlay HTML objects over my SVG.

My current strategy is creating an empty DIV next to the SVG element called .html-overlay. I position it according to the internal padding I set in the SVG for my main graphic (ex: 20px). Then I use the following function (with jQuery) to figure out where the HTML element should go:

//element is the object returned by D3 .append()
var getOffset: function(element) {
        var $element = $(element[0][0]);
        return {
            left: $element.offset().left - $('.html-overlay').offset().left,
            top: $element.offset().top - $('.html-overlay').offset().top
        };
    }

I wonder, there MUST be some internal (non-jQuery dependant) way to quickly get an element's offset. It's very useful (especially after an elements goes through multiple translations, rotations, scales, etc.)

It would also be great to have functions for figuring out the offset of the "center" of an element, the topmost point of element, bottommost, leftmost, rightmost, etc.

NOTE:

The getBoundingClientRect() doesn't give the correct numbers for some reason:

var $element = $(element[0][0]);

            console.log($element.offset(), element[0][0].getBoundingClientRect())
Object
left: 328
top: 248.8333282470703
__proto__: Object

ClientRect
bottom: 376.83331298828125
height: 139.99998474121094
left: 328
right: 478
top: 236.8333282470703
width: 150

Upvotes: 23

Views: 28529

Answers (3)

pistol-pete
pistol-pete

Reputation: 1251

Extending James Lai's answer to support modern versions of IE:

function getVpPos(el) {
    if(el.parentNode.nodeName === 'svg') {
        return el.parentNode.getBoundingClientRect();
    }
    return getVpPos(el.parentNode);
}

Note: parentElement is changed to parentNode and tagName is changed to nodeName.

Upvotes: 0

James Lai
James Lai

Reputation: 2071

The problem is with getBoundingClientRect, which doesn't account for scroll position or the element's container position relative to the document. It will only report back that item's exact position relative to the document top and left coordinates.

I've created a d3 method which will report back the position of the element. It works by looping through parent elements until it finds the container SVG, then considers that item's position in the calculations. It returns what you would normally receive from getBoundingClientRect.

Here's the method:

d3.selection.prototype.position = function() {

    var el = this.node();
    var elPos = el.getBoundingClientRect();
    var vpPos = getVpPos(el);

    function getVpPos(el) {
        if(el.parentElement.tagName === 'svg') {
            return el.parentElement.getBoundingClientRect();
        }
        return getVpPos(el.parentElement);
    }

    return {
        top: elPos.top - vpPos.top,
        left: elPos.left - vpPos.left,
        width: elPos.width,
        bottom: elPos.bottom - vpPos.top,
        height: elPos.height,
        right: elPos.right - vpPos.left
    };

};

And you can use this like so:

d3.select(element).position()

Note that I haven't actually added the code here to consider the scroll position.

Upvotes: 11

Asain Kujovic
Asain Kujovic

Reputation: 1829

did you try

var xywh =element[0][0].getBoundingClientRect();

seems to have everything in it? (original soution is in this post)

Upvotes: 17

Related Questions