I want to display an overlay (html div) when a user clicks on an element in an SVG diagram. To visualize the problem I'm having, suppose that the SVG image has a horizontal row of 6 elements. At the click event, I get the element's coordinates and use them to display the overlay next to it. The problem is that as I click the elements from left to right, I notice that the horizontal offset between the element and the overlay keeps getting smaller. That is, the 6th element displays the overlay much closer to it than the first element. This happens in both Chrome and FF, and it's an issue because sometime the overlay covers the element itself.
At first I was using JQuery's position() property, which didn't exhibit the behavior that I described above, but it returned very different values in Chrome and Firefox, plus it is not officially supported by JQuery on svg elements. So I tried with DOM's standard offsetLeft and offsetTop, as well as svg's x.animVal.value property and various libraries that I found on the web, but they all have the same erratic offset problem. I presume that this happens because the svg image is scaled, so I'm looking for I way to just get an svg's element position relative to the actual html document which contains it. Is there a way to do this?
In case you haven't worked something out since March (and for anyone else having this problem), try getBoundingClientRect() on your SVG node.
Returns a ClientRect object that gives you top, bottom, left, right, width, and height relative to the document. Was able to use this to position Twitter Bootstrap popovers (divs) next to SVG rects.
jQuery's position() does not work well for SVG elements. There is a ticket for that.
You can use the native SVG method getBBox() to get the position of a SVG element.
Example
$('svg circle')[0].getBBox();
You can get the position coordinate relative to the page of any element, also <svg>, with this little function:
function getOffset(element)
{
var bound = element.getBoundingClientRect();
var html = document.documentElement;
return {
top: bound.top + window.pageYOffset - html.clientTop,
left: bound.left + window.pageXOffset - html.clientLeft
};
}
var offset = getOffset(svg);var x = offset.left;var y = offset.top;
live demo: https://codepen.io/martinwantke/pen/rpNLWr
Related
I have a simple canvas drawing program that works perfectly.
But when I want to use it on my webpage I have to position it. I need canvas element to be inside 1 div element and i need to position that div element in css. When I do change position of parent div element my canvas window moves regularly but when I try to draw, mouse position of my cursor is not matching line on the screen.
I would like to know how to solve this problem and how to position my canvas window correctly where I want.
Here is canvas program that works(color buttons are not positioned well but nevermind).
HjD7e -> jsfiddle id
And here is the one that is messed up(cursor position is not matching)
S6Dhe -> jsfiddle id
Since you are now offsetting multiple elements, you'll need to take the new positions into account. Seeing as you are positioning the parent container, it might be easier to put all your positioning there and remove the extra positioning from the canvas itself. Using:
#contain{
position:absolute;
top:300px;
left:500px;
width: 400px;
height: 400px
}
instead. From there, change the code getting the positions to:
contain = document.getElementById('contain');
currX = e.clientX - contain.offsetLeft + window.scrollX;
currY = e.clientY - contain.offsetTop + window.scrollY;
also accounting for scrolling in the window.
The working fiddle is here. (Caveat, only tested in Firefox)
I am using jQuery offset().top to calculate a hyperlink's pixels from the top of the document window so that when hovered, a tooltip can appear above it.
By default, the tooltip's css has an absolute position of top 0 and left 0. When the link is hovered, it calculates it's position from the top of the document and uses that as the css top position for the tooltip.
The problem is that on some pages, offset is calculating the link's position perfectly, and on others, it is around 50 pixels too many. I can't figure out why.
$(".tiptrigger").mouseenter(function() {
var s_id = $(this).attr('id');
var calc = $(this).offset().top;
$("#tip-"+s_id).css({"margin-top": calc+"px"});
});
Instead of absolute try to use fixed for your popup element. Than offset().top should work if you don't have any unnecessary padding applied to body
If still inaccurate instead of .offset().top give a chance to https://developer.mozilla.org/en-US/docs/Web/API/element.getBoundingClientRect
.offset() has this limitation:
Note: jQuery does not support getting the offset coordinates of hidden elements or accounting for borders, margins, or padding set on the body element.
So your jQuery is probably correct but if you have some padding/margin on your body, you will experience an incorrect offset. Either remove it or include it in your calculation:
var bodyOffset = $('body').css("margin-top") + $('body').css('padding-top') + $('body').css('border-top');
var calc = $(this).offset().top + bodyOffset;
I'm trying to determine what would be the best way to know the "visual" position (not the DOM tree position) of an element in relation to another element.
For example, I have an array of elements, and I have a slected element, and want to know which of those elements are visually positioned to the right of the selected element.
Also note that the elements can be of any tag type, and may have any position (relative, absolute, fixed) and display (as long as it's visible).
Any jQuery solutions are welcome.
Why not simply use element.offsetTop and element.offsetLeft where offsetTop is the y-axis, and offsetLeft is the x-axis.
No jQuery needed at all, though if you insist $(element).offset(); will probably do.
Note that the top-left corner of the window is the 0-0 point. elements "stuck" to the left will have offsetLeft === 0, the ones to tsticking to the top have offsetTop === 0. That's all there is too it, really
In response to zeaklous' comment:
To get the dimensions of an object:
var dimension = {top: {x: element.offsetLeft, y: element.offsetTop},
bottom: {x: element.offsetWidth, y: element.offsetHeight}
};
Be advised, that offsetWidth and offsetHeight have been known to return 0 in some cases where the DOM has just been redrawn (getting these properties from an element you've just altered can cause this).
To avoid that, some people tend to use setTimeout(); when getting the offset of an element that they suspect might just have been altered.
In response to the comments, I've been pointed out that you can simply use getBoundingClientRect to avoid any issues with the offsetX properties:
document.getElementById('foo').getBoundingClientRect();
This returns an instance of the ClientRect object, with which you can play. Check MDN for details
How can I possibly change Javascript's origin? For example, if I were to call offsetTop on a div, it's really tell me how far it is from the origin. Javascript's origin is at the top left of the page, but how can I move that?
I want to make it at the bottom left.
Your information about offsetTop is incorrect. From MDC:
offsetTop returns the distance of the current element relative to the top of the offsetParent node.
You cannot "move" the origin in the way you are thinking, though you could use a bit of math to compute the distance from the element's top edge to the bottom of the screen/page. Using jQuery (for clarity, so the code isn't mucked up with cross-browser feature detection):
var $doc = $(document),
docHeight = $doc.height(),
$elt = $('#some-element-id'),
top = $elt.offset().top,
// this is the value you're interested in
originShiftedTop = docHeight - top;
Demo. Try re-running the fiddle with different Result pane heights.
Note that jQuery's .offset() returns the position of the element relative to the document, unlike element.offsetTop the DOM-standard property. jQuery's .position() function behaves like element.offsetTop.
I've got a div that uses overflow:auto to keep the contents inside the div as it is resized and dragged around the page. I'm using some ajax to retrieve lines of text from the server, then append them to the end of the div, so the content is growing downwards. Every time this happens, I'd like to use JS to scroll the div to the bottom so the most recently added content is visible, similar to the way a chat room or command line console would work.
So far I've been using this snippet to do it (I'm also using jQuery, hence the $() function):
$("#thediv").scrollTop = $("#thediv").scrollHeight;
However it's been giving me inconsistent results. Sometimes it works, sometimes not, and it completely ceases to work if the user ever resizes the div or moves the scroll bar manually.
The target browser is Firefox 3, and it's being deployed in a controlled environment so it doesn't need to work in IE at all.
Any ideas guys? This one's got me stumped. Thanks!
scrollHeight should be the total height of content. scrollTop specifies the pixel offset into that content to be displayed at the top of the element's client area.
So you really want (still using jQuery):
$("#thediv").each( function()
{
// certain browsers have a bug such that scrollHeight is too small
// when content does not fill the client area of the element
var scrollHeight = Math.max(this.scrollHeight, this.clientHeight);
this.scrollTop = scrollHeight - this.clientHeight;
});
...which will set the scroll offset to the last clientHeight worth of content.
scrollIntoView
The scrollIntoView method scrolls the element into view.
Using a loop to iterate over a jQuery of one element is quite inefficient. When selecting an ID, you can just retrieve the first and unique element of the jQuery using get() or the [] notation.
var div = $("#thediv")[0];
// certain browsers have a bug such that scrollHeight is too small
// when content does not fill the client area of the element
var scrollHeight = Math.max(div.scrollHeight, div.clientHeight);
div.scrollTop = scrollHeight - div.clientHeight;
$("#thediv").scrollTop($("#thediv")[0].scrollHeight);
It can be done in plain JS. The trick is to set scrollTop to a value equal or greater than the total height of the element (scrollHeight):
const theDiv = document.querySelector('#thediv');
theDiv.scrollTop = Math.pow(10, 10);
From MDN:
If set to a value greater than the maximum available for the element,
scrollTop settles itself to the maximum value.
While the value of Math.pow(10, 10) did the trick using a too high value like Infintiy or Number.MAX_VALUE will reset scrollTop to 0 (Firefox 66).
I had a div wrapping 3 divs that were floating left, and whose contents were being resized. It helps to turn funky-colored borders/background on for the div-wrapper when you try to resolve this. The problem was that the resized div-content was overflowing outside the div-wrapper (and bled to underneath the area of content below the wrapper).
Resolved by using #Shog9's answer above. As applied to my situation, this was the HTML layout:
<div id="div-wrapper">
<div class="left-div"></div>
<div id="div-content" class="middle-div">
Some short/sweet content that will be elongated by Jquery.
</div>
<div class="right-div"></div>
</div>
This was the my jQuery to resize the div-wrapper:
<script>
$("#div-content").text("a very long string of text that will overflow beyond the width/height of the div-content");
//now I need to resize the div...
var contentHeight = $('#div-content').prop('scrollHeight')
$("#div-wrapper").height(contentHeight);
</script>
To note, $('#div-content').prop('scrollHeight') produces the height that the wrapper needs to resize to. Also I am unaware of any other way to obtain the scrollHeight an actual jQuery function; Neither of $('#div-content').scrollTop() and $('#div-content').height would produce the real content-height values. Hope this helps someone out there!