Getting offsetTop of element in a table - javascript

I can't seem to figure out how to get the offsetTop of an element within a table. It works fine on elements outside tables, but all of the elements within a table return the same result, and it's usually at the top of the page. I tried this in Firefox and Chrome. How do I get the offsetTop of an element in a table?

offsetTop returns a value relative to offsetParent; you need to recursively add offsetParent.offsetTop through all of the parents until offsetParent is null. Consider using jQuery's offset() method.
EDIT: If you don't want to use jQuery, you can write a method like this (untested):
function offset(elem) {
if(!elem) elem = this;
var x = elem.offsetLeft;
var y = elem.offsetTop;
while (elem = elem.offsetParent) {
x += elem.offsetLeft;
y += elem.offsetTop;
}
return { left: x, top: y };
}

Related

Getting coordinates of objects in JS

So based on this question I asked, what's the most reliable way of getting position of objects that's crossbrowser? Thx
In general, assuming you have an element named elem, it's actually quite easy to get the X and Y coordinates of the top-left corners of an element, assuming you want these in document coordinates. In all browsers this is returned by the elem.offsetLeft and elem.offsetTop properties.
The only trick you have to be aware of is that if elem is absolutely positioned in another element, say a div with a left / top margin of 20px, these properties will return 0, as it only takes into account the current element and not the entire chain of elements. Luckily we can use a "chain-traversal" function to capture all of the margins of elements associated with a given element, tally them up to get the correct document coordinates.
As Sime Vidas mentioned, there is also JQuery's position() and offset() properties, in this case you would want the offset() properties.
You can also use the getBoundingClientRect() method, however this returns the coordinates of an element relative to its offsetParent and thus is not entirely reliable. Look at the following examples:
// getPosition function
function getPosition(elem){
var dims = {offsetLeft:0, offsetTop:0};
do {
dims.offsetLeft += elem.offsetLeft;
dims.offsetTop += elem.offsetTop;
}
while (elem = elem.offsetParent);
return dims;
}
cont1.style.position = "absolute";
cont1.style.marginLeft = "10px";
cont2.style.position = "absolute";
cont2.style.marginLeft = "10px";
box.style.position = "absolute";
box.style.marginLeft = "10px";
console.log(getPosition(box).offsetLeft); // returns "30"
console.log(getPosition(box).offsetTop); // returns "0"
// or in JQuery
console.log($(box).offset().left) // also returns "30"
console.log($(box).offset().top) // also returns "0"
Also I suggest you read this.
If you want to find the position of an element relative to document use jQuery offset() method.
var p = $("p:last");
var offset = p.offset();
p.html( "left: " + offset.left + ", top: " + offset.top );
.offset() reference: http://api.jquery.com/offset/
If you want to find the poistion of an element relative to its parent then use jQuery position() method.
var p = $("p:first");
var position = p.position();
$("p:last").text( "left: " + position.left + ", top: " + position.top );
.position() reference: http://api.jquery.com/position/
And these methods almost gives perfect result in most of the browsers.
I like element.getBoundingClientRect(). It has good cross-browser support.
var coords = element.getBoundingClientRect();
This gives the coordinates relative to the viewport. To get the coordinates relative to the document, add document.documentElement.scrollTop to the top and document.documentElement.scrollLeft to the left.
coords.top += document.documentElement.scrollTop;
coords.left += document.documentElement.scrollLeft;
But since you are already using jQuery, you may as well just use .offset().

Can't get the initial values of an HTML CSS Element using javascript

I'm trying to manipulate a div element. Although I have defined certain property values, I can't seem to get the initial values.
Here's the problem: Fiddle
I'm at the end of my rope... Thanks.
I think you have to use document.styleSheets, find your stylesheet, then loop through the cssRules(IE rules) list to match a selector to "#viewbase" and then you can access your style properties like you would have with inline styles, since the style info in there is also a CSSStyleDeclaration ( so you can use element.style.left and so on).
Sidenode: getComputedStyle doesn't work on most versions of IE. IE has currentStyle properties for each element, but it's not the same thing. Maybe a combination of both will also work.
UPDATE
Managed to get the initial positions, if that's what you need, you can reassign these values if i remember well.
function trigger(e) {
item = this;
if (!e) var e = window.event;
if (e.pageX || e.pageY) {
x1 = e.pageX;
y1 = e.pageY;
}
else if (e.clientX || e.clientY) {
x1 = e.clientX;
y1 = e.clientY;
}
document.getElementById("ex").innerHTML = x1;
document.getElementById("wye").innerHTML= y1;
// the stylesheet you defined, in a standalone context, it might
// have another index, (i.e. 0 if it's the only one)
list = document.styleSheets[2].cssRules;
for(var i=0;i<list.length;i++)
//match #viewbase
if(list[i].selectorText.toLowerCase() == "#"+item.id)
{ item = list[i];
break;
}
document.getElementById("xval").innerHTML = item.style.left;
document.getElementById("yval").innerHTML = item.style.top;
document.getElementById("parseX").innerHTML = parseInt(item.style.left, 10);
document.getElementById("parseY").innerHTML = parseInt(item.style.top, 10);
document.getElementById("debug").innerHTML = 'clicked!';
document.onmouseup = release;
}
The other version would be to use the following
var styleDef = window.getComputedStyle(item) || item.currentStyle;
document.getElementById("xval").innerHTML = styleDef.left;
document.getElementById("yval").innerHTML = styleDef.top;
The problem is that your style is not defined within the html but comes from css. In that case you need to use getComputedStyle to retrieve the styling information. Do something like
document.defaultView.getComputedStyle(item,null)
rather than item.style.
How about using item.offsetLeft and item.offsetTop. This will include margins and padding, but you should be able to easily compensate for this fact. Here is an updated JSFiddle.
document.getElementById("xval").innerHTML = item.offsetLeft;
document.getElementById("yval").innerHTML = item.offsetTop;
Again, in this case, these calls return 10, 10 respectively, instead of 0, 0, which you are looking for. That said, is this enough to move you past your issue?

jQuery - piece of code causing conflict problems on pages the selector doesn't exist

Okay so I am developing a WordPress theme. On the single post page I have a comments div which floats down the page using some jquery. I am also running a modal popup form to log in. This is completely fine on the single page when the #commentWrapper (selector for the jquery floating effect) exists. However on pages where there is no #commentWrapper to float, the modal form doesn't work. I pinned down the problem to this line in my general jQuery call (by removing each line and testing).
Call in general.js, very last call:
jQuery('#commentWrapper').stickyfloat({ duration: 300, easing : 'easeInQuad' });
Actual plugin it refers to:
$.fn.stickyfloat = function(options, lockBottom) {
var $obj = this;
var parentPaddingTop = parseInt($obj.parent().css('padding-top'));
var startOffset = $obj.parent().offset().top;
var opts = $.extend({ startOffset: startOffset, offsetY: parentPaddingTop, duration: 200, lockBottom:true }, options);
$obj.css({ position: 'absolute' });
if(opts.lockBottom){
var bottomPos = $obj.parent().height() - $obj.height() + parentPaddingTop; //get the maximum scrollTop value
if( bottomPos < 0 )
bottomPos = 0;
}
$(window).scroll(function () {
$obj.stop(); // stop all calculations on scroll event
var pastStartOffset = $(document).scrollTop() > opts.startOffset; // check if the window was scrolled down more than the start offset declared.
var objFartherThanTopPos = $obj.offset().top > startOffset; // check if the object is at it's top position (starting point)
var objBiggerThanWindow = $obj.outerHeight() < $(window).height(); // if the window size is smaller than the Obj size, then do not animate.
// if window scrolled down more than startOffset OR obj position is greater than
// the top position possible (+ offsetY) AND window size must be bigger than Obj size
if( (pastStartOffset || objFartherThanTopPos) && objBiggerThanWindow ){
var newpos = ($(document).scrollTop() -startOffset + opts.offsetY );
if ( newpos > bottomPos )
newpos = bottomPos;
if ( $(document).scrollTop() < opts.startOffset ) // if window scrolled < starting offset, then reset Obj position (opts.offsetY);
newpos = parentPaddingTop;
$obj.animate({ top: newpos }, opts.duration );
}
});
};
If I add an if command to see if the selector exists, it all works. I would like to know what the problem is for future website however.
Well, your stickyfloat() method assumes many things, like always being called on a jQuery object that contains at least one element, or that element always having a parent. For instance, consider the following code:
var $obj = this;
// ...
var startOffset = $obj.parent().offset().top;
If the jQuery object your method is called on is empty, or if its first element has no parent (the method was called on $("html")), the code will fail because parent().offset() will be null.
If you want your method to be more robust, you should not assume anything about the object it's called on. A good first step is to make the method chainable, which is always beneficial to your users and will get rid of the first problem. The recommended way of doing that is:
$.fn.stickyfloat = function(options, lockBottom) {
return this.each(function() {
var $obj = $(this);
// The rest of your code.
});
};
Since the code now runs sequentially on each element (if any) through an anonymous function, testing for the parent element's existence can be dealt with by returning early:
var $obj = $(this);
var $parent = $obj.parent();
if (!$parent.length) {
return; // No parent, continue with next element, if any.
}
// Parent element is safe to use.
var parentPaddingTop = parseInt($parent.css('padding-top'));
var startOffset = $parent.offset().top;

How to get content currently being displayed in browser viewport

How can I get an indication of what part of a long document is currently being displayed?
E.g. if my html contains 1,000 lines
1
2
3
...
999
1000
and the user is near the middle showing the 500th line then I would like to get "500\n501\n502" or something like that.
Obviously most scenarios would be more complex than this, but my requirement is to find which text is currently being displayed in the browser viewport so I can show a status value appropriate to the current text.
Thanks
Martin
If you have jQuery, you can use this function to check if a DOM element is currently shown in the viewport:
function isInView(elem) {
var docViewTop = $(window).scrollTop();
var docViewBottom = docViewTop + $(window).height();
var elemTop = $(elem).offset().top;
var elemBottom = elemTop + $(elem).height();
return ((elemBottom >= docViewTop) && (elemTop <= docViewBottom));
}
You can get a value in pixels from the scrollTop property:
document.body.scrollTop = 40;
To know what part of your document that is visible, you could loop through (say) all p-tags until you find one with a negative scrollTop value. The one before that is the one at the top of the window.
I've just seen a piece of sample code on msdn
function isinView(oObject)
{
var oParent = oObject.offsetParent;
var iOffsetTop = oObject.offsetTop;
var iClientHeight = oParent.clientHeight;
if (iOffsetTop > iClientHeight) {
alert("Special Text not in view. Expand Window to put Text in View.");
}
else{
alert("Special Text in View!");
}
}
Yes, there is a way. I will use YUI's API to illustrate my example. First your text must be in some sort of dom element, whether its a span, div, p or anything, it must be in a element. Here I will assume list item
var viewPortY = YAHOO.util.Dom.getDocumentScrollTop(),
viewPortHeight = YAHOO.util.Dom.getViewportHeight(), i = 0,
// get all the dom elements that contain the text, sorry if this isn't exact, its just a rough example
items = YAHOO.util.Dom.getElementBy(null, 'li', document.getElementById('item-container')),
viewedItems = [];
for (i = 0 ; i < items.length; i++) {
var y = YAHOO.util.Dom.getY(items[i])
if (y > viewPortY && y < (viewPortY + viewPortHeight)) {
viewedItems.push(items[i])
}
}
So essentially, I get all the dom objects that contain the text your interested in. I then loop through, and whoever's Y co-ordinate is between the viewports Y and Y + ViewPort Height, I put in an array.
I implemented what I thought was a more optimal solution for my environment:
I am writing for Android so I can easily interact with a Java class from javascript. My actual solution involved getting offsetTop of all tags I am interested in and passing the offsets to java.
Also registering an onscroll handler that passed window.pageYOffset throught to the same Java class. Then the java class can compare offsetTop of each tag with pageYOffset to see which tag is at the top of the current viewport.

Get position of element by JavaScript

I've seen dozens of scripts that can catch the x and y position of an element/object within the page. But I am always having trouble with catching the x and y when the webpage is using margins at the body, or other elements, absolute/relative elements, such like that.
Is there a solution which provides the exact position, no matter what margins or paddings are used?
I use following code to move div box to follow cursor in this Web IME site
function xy(x) {
o = document.getElementById(x);
var l =o.offsetLeft; var t = o.offsetTop;
while (o=o.offsetParent)
l += o.offsetLeft;
o = document.getElementById(x);
while (o=o.offsetParent)
t += o.offsetTop;
return [l,t];
}
Its return an array [left,top],
Getting the exact position is simply a matter of adding the offsetLefts and offsetTops recursively to the offsetParents:
function getPos(ele){
var x=0;
var y=0;
while(true){
x += ele.offsetLeft;
y += ele.offsetTop;
if(ele.offsetParent === null){
break;
}
ele = ele.offsetParent;
}
return [x, y];
}
Btw, this solution would probably run twice faster than the other solution above since we only loop once.
offsetParent and other offset functions are old... use the getBoundingClientRect
function... use this:
function getAbsPosition(element) {
var rect = element.getBoundingClientRect();
return {x:rect.left,y:rect.top}
}
now you can use it like this
<div style="margin:50px;padding:50px;" id="myEl">lol</div>
<script type="text/javascript">
var coords = getAbsPosition( document.getElementById('myEl') );
alert( coords.x );alert( coords.y );
</script>
Don't worry... no matter how much margin or position or padding the element has, it always works
Here are some improvements on #Pacerier 's code in my own answer:
function getPos(el, rel)
{
var x=0, y=0;
do {
x += el.offsetLeft;
y += el.offsetTop;
el = el.offsetParent;
}
while (el != rel)
return {x:x, y:y};
}
If is just used as getPos(myElem) will return global position. If a second element is included as an argument (i.e. getPos(myElem, someAncestor)) that is an ancestor/parent of the first (at least somewhere up the chain) then it will give the position relative to that ancestor. If rel is not given (i.e. is left undefined), then it purposefully uses != instead of !== because it should stop when el gets to null and rel is undefined as well, so the non-strict equality is purposeful, don't change it. It also returns an object, since that's a bit more readable in usage than an array (and so you can do something like getPos().x).
This is derived from Pacerier's solution, so if you're gonna upvote this, consider upvoting his too.

Categories

Resources