Related
StackOverflow is loaded with questions about how to check if an element is really visible in the viewport, but they all seek for a boolean answer. I'm interested in getting the element's actual areas that are visible.
function getVisibleAreas(e) {
...
return rectangleSet;
}
Putting it more formally - the visible areas of elements is the set of (preferably non-overlapping) rectangles in CSS coordinates for which elementFromPoint(x, y) will return the element if the point (x, y) is contained in (at least) one of the rectangles in the set.
The outcome of calling this function on all DOM elements (including iframes) should be a set of non-overlapping area sets which union is the entire viewport area.
My goal is to create some kind of a viewport "dump" data structure, which can efficiently return a single element for a given point in the viewport, and vice versa - for a given element in the dump, it will return the set of visible areas.
(The data structure will be passed to a remote client application, so I will not necessarily have access to the actual document when I need to query the viewport structure).
Implementation requirements:
Obviously, the implementation should consider element's hidden state, z-index, header & footer etc.
I am looking for an implementation that works in all common used browsers, especially mobile - Android's Chrome and iOS's Safari.
Preferably doesn't use external libraries.
Of course, I could be naïve and call elementFromPoint for every discrete point in the viewport, But performance is crucial since I iterate over all of the elements, and will do it quite often.
Please direct me as to how I can achieve this goal.
Disclaimer: I'm pretty noob to web programming concepts, so I might have used wrong technical terms.
Progress:
I came up with an implementation. The algorithm is pretty simple:
Iterate over all elements, and add their vertical / horizontal lines to a coordinates map (if the coordinate is within the viewport).
Call `document.elementFromPoint` for each "rectangle" center position. A rectangle is an area between two consecutive vertical and two consecutive horizontal coordinates in the map from step 1.
This produces a set of areas / rectangles, each pointing to a single element.
The problems with my implementation are:
It is inefficient for complicated pages (can take up to 2-4 minutes for a really big screen and gmail inbox).
It produces a large amount of rectangles per a single element, which makes it inefficient to stringify and send over a network, and also inconvenient to work with (I would want to end up with a set with as few rectangles as possible per element).
As much as I can tell, the elementFromPoint call is the one that takes a lot of time and causes my algorithm to be relatively useless...
Can anyone suggest a better approach?
Here is my implementation:
function AreaPortion(l, t, r, b, currentDoc) {
if (!currentDoc) currentDoc = document;
this._x = l;
this._y = t;
this._r = r;
this._b = b;
this._w = r - l;
this._h = b - t;
center = this.getCenter();
this._elem = currentDoc.elementFromPoint(center[0], center[1]);
}
AreaPortion.prototype = {
getName: function() {
return "[x:" + this._x + ",y:" + this._y + ",w:" + this._w + ",h:" + this._h + "]";
},
getCenter: function() {
return [this._x + (this._w / 2), this._y + (this._h / 2)];
}
}
function getViewport() {
var viewPortWidth;
var viewPortHeight;
// IE6 in standards compliant mode (i.e. with a valid doctype as the first line in the document)
if (
typeof document.documentElement != 'undefined' &&
typeof document.documentElement.clientWidth != 'undefined' &&
document.documentElement.clientWidth != 0) {
viewPortWidth = document.documentElement.clientWidth,
viewPortHeight = document.documentElement.clientHeight
}
// the more standards compliant browsers (mozilla/netscape/opera/IE7) use window.innerWidth and window.innerHeight
else if (typeof window.innerWidth != 'undefined') {
viewPortWidth = window.innerWidth,
viewPortHeight = window.innerHeight
}
// older versions of IE
else {
viewPortWidth = document.getElementsByTagName('body')[0].clientWidth,
viewPortHeight = document.getElementsByTagName('body')[0].clientHeight
}
return [viewPortWidth, viewPortHeight];
}
function getLines() {
var onScreen = [];
var viewPort = getViewport();
// TODO: header & footer
var all = document.getElementsByTagName("*");
var vert = {};
var horz = {};
vert["0"] = 0;
vert["" + viewPort[1]] = viewPort[1];
horz["0"] = 0;
horz["" + viewPort[0]] = viewPort[0];
for (i = 0 ; i < all.length ; i++) {
var e = all[i];
// TODO: Get all client rectangles
var rect = e.getBoundingClientRect();
if (rect.width < 1 && rect.height < 1) continue;
var left = Math.floor(rect.left);
var top = Math.floor(rect.top);
var right = Math.floor(rect.right);
var bottom = Math.floor(rect.bottom);
if (top > 0 && top < viewPort[1]) {
vert["" + top] = top;
}
if (bottom > 0 && bottom < viewPort[1]) {
vert["" + bottom] = bottom;
}
if (right > 0 && right < viewPort[0]) {
horz["" + right] = right;
}
if (left > 0 && left < viewPort[0]) {
horz["" + left] = left;
}
}
hCoords = [];
vCoords = [];
//TODO:
for (var v in vert) {
vCoords.push(vert[v]);
}
for (var h in horz) {
hCoords.push(horz[h]);
}
return [hCoords, vCoords];
}
function getAreaPortions() {
var portions = {}
var lines = getLines();
var hCoords = lines[0];
var vCoords = lines[1];
for (i = 1 ; i < hCoords.length ; i++) {
for (j = 1 ; j < vCoords.length ; j++) {
var portion = new AreaPortion(hCoords[i - 1], vCoords[j - 1], hCoords[i], vCoords[j]);
portions[portion.getName()] = portion;
}
}
return portions;
}
Try
var res = [];
$("body *").each(function (i, el) {
if ((el.getBoundingClientRect().bottom <= window.innerHeight
|| el.getBoundingClientRect().top <= window.innerHeight)
&& el.getBoundingClientRect().right <= window.innerWidth) {
res.push([el.tagName.toLowerCase(), el.getBoundingClientRect()]);
};
});
jsfiddle http://jsfiddle.net/guest271314/ueum30g5/
See Element.getBoundingClientRect()
$.each(new Array(180), function () {
$("body").append(
$("<img>"))
});
$.each(new Array(180), function () {
$("body").append(
$("<img>"))
});
var res = [];
$("body *").each(function (i, el) {
if ((el.getBoundingClientRect().bottom <= window.innerHeight || el.getBoundingClientRect().top <= window.innerHeight)
&& el.getBoundingClientRect().right <= window.innerWidth) {
res.push(
[el.tagName.toLowerCase(),
el.getBoundingClientRect()]);
$(el).css(
"outline", "0.15em solid red");
$("body").append(JSON.stringify(res, null, 4));
console.log(res)
};
});
body {
width : 1000px;
height : 1000px;
}
img {
width : 50px;
height : 50px;
background : navy;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
I don't know if the performance will be sufficient (especially on a mobile device), and the result is not quite a rectangle-set as you requested, but did you consider using a bitmap to store the result?
Note some elements may have 3d css transform (eg. skew, rotate), some elements may have border radius, and some elements may have invisible background - if you want to include these features as well for your "element from pixel" function then a rectangle set can't help you - but the bitmap can accommodate all of the visual features.
The solution to generate the bitmap is rather simple (I imagine... not tested):
Create a Canvas the size of the visible screen.
iterate over all the elements recursively, sorted by z-order, ignore hidden
for each element draw a rectangle in the canvas, the color of the of the rectangle is an identifier of the element (eg. could be incremental counter). If you want you can modify the rectangle based on the visual features of the element (skew, rotate, border radius, etc...)
save the canvas as lossless format, eg png not jpg
send the bitmap as the meta data of elements on screen
To query which element is at point (x,y) you could check the color of the bitmap at pixel (x,y) and the color will tell you what is the element.
If you can jettison IE, here's a simple one:
function getElementVisibleRect(el) {
return new Promise((resolve, reject) => {
el.style.overflow = "hidden";
requestAnimationFrame((timeStamp) => {
var br = el.getBoundingClientRect();
el.style.overflow = "";
resolve(br);
});
});
}
Even then, Promises are easily polyfillable and requestAnimationFrame() works as far back as IE 8. And by 2016, the only thing you should bother to give any poor souls on older IE is a legible experience.
I'm trying to create a image cropper, that's based on a image inside a container box. The user should be able to drag or move the image and pinch to zoom.
Currently, I've been working in the drag functionality, but I find that's not working properly. When going from Top left and drag to Bottom right works more or less ok, but if I do try to drag from left to right or the inverse, the image keeps bouncing back to the initial drag position.
My code is really basic, where you can see that I just add to x or y the deltaY/X, which I seem would be more then enough ?
var x = 0;
var y = 0;
var imgEl = document.querySelectorAll("img")[0];
Hammer(element).on("dragstart", function(event) {
imgEl = document.querySelectorAll("img")[0];
y = parseFloat(imgEl.style.top) || 0;
x = parseFloat(imgEl.style.top) || 0;
});
Hammer(element).on("drag", function(event) {
imgEl.style.top = (y + parseFloat(event.gesture.deltaY))+'px';
imgEl.style.left = (x + parseFloat(event.gesture.deltaX))+'px';
});
I've got a demo setup here: http://jsbin.com/kimes/4
When testing I've been noticing that, when I drag from Bottom right to Top Left works fine!
Thanks for looking!
You need to add new mouse position to the dragging variable.
Oh, sorry about taking you guys time, I had a typo:
y = parseFloat(imgEl.style.top) || 0;
x = parseFloat(imgEl.style.top) || 0;
Should be:
y = parseFloat(imgEl.style.top) || 0;
x = parseFloat(imgEl.style.left) || 0;
I am creating a new "whack-a-mole" style game where the children have to hit the correct numbers in accordance to the question. So far it is going really well, I have a timer, count the right and wrong answers and when the game is started I have a number of divs called "characters" that appear in the container randomly at set times.
The problem I am having is that because it is completely random, sometimes the "characters" appear overlapped with one another. Is there a way to organize them so that they appear in set places in the container and don't overlap when they appear.
Here I have the code that maps the divs to the container..
function randomFromTo(from, to) {
return Math.floor(Math.random() * (to - from + 1) + from);
}
function scramble() {
var children = $('#container').children();
var randomId = randomFromTo(1, children.length);
moveRandom('char' + randomId);
}
function moveRandom(id) {
var cPos = $('#container').offset();
var cHeight = $('#container').height();
var cWidth = $('#container').width();
var pad = parseInt($('#container').css('padding-top').replace('px', ''));
var bHeight = $('#' + id).height();
var bWidth = $('#' + id).width();
maxY = cPos.top + cHeight - bHeight - pad;
maxX = cPos.left + cWidth - bWidth - pad;
minY = cPos.top + pad;
minX = cPos.left + pad;
newY = randomFromTo(minY, maxY);
newX = randomFromTo(minX, maxX);
$('#' + id).css({
top: newY,
left: newX
}).fadeIn(100, function () {
setTimeout(function () {
$('#' + id).fadeOut(100);
window.cont++;
}, 1000);
});
I have a fiddle if it helps.. http://jsfiddle.net/pUwKb/8/
As #aug suggests, you should know where you cannot place things at draw-time, and only place them at valid positions. The easiest way to do this is to keep currently-occupied positions handy to check them against proposed locations.
I suggest something like
// locations of current divs; elements like {x: 10, y: 40}
var boxes = [];
// p point; b box top-left corner; w and h width and height
function inside(p, w, h, b) {
return (p.x >= b.x) && (p.y >= b.y) && (p.x < b.x + w) && (p.y < b.y + h);
}
// a and b box top-left corners; w and h width and height; m is margin
function overlaps(a, b, w, h, m) {
var corners = [a, {x:a.x+w, y:a.y}, {x:a.x, y:a.y+h}, {x:a.x+w, y:a.y+h}];
var bWithMargins = {x:b.x-m, y:b.y-m};
for (var i=0; i<corners.length; i++) {
if (inside(corners[i], bWithMargins, w+2*m, h+2*m) return true;
}
return false;
}
// when placing a new piece
var box;
while (box === undefined) {
box = createRandomPosition(); // returns something like {x: 15, y: 92}
for (var i=0; i<boxes.length; i++) {
if (overlaps(box, boxes[i], boxwidth, boxheight, margin)) {
box = undefined;
break;
}
}
}
boxes.push(box);
Warning: untested code, beware the typos.
The basic idea you will have to implement is that when a random coordinate is chosen, theoretically you SHOULD know the boundaries of what is not permissible and your program should know not to choose those places (whether you find an algorithm or way of simply disregarding those ranges or your program constantly checks to make sure that the number chosen isn't within the boundary is up to you. the latter is easier to implement but is a bad way of going about it simply because you are entirely relying on chance).
Let's say for example coordinate 50, 70 is selected. If the picture is 50x50 in size, the range of what is allowed would exclude not only the dimensions of the picture, but also 50px in all directions of the picture so that no overlap may occur.
Hope this helps. If I have time, I might try to code an example but I hope this answers the conceptual aspect of the question if that is what you were having trouble with.
Oh and btw forgot to say really great job on this program. It looks awesome :)
You can approach this problem in at least two ways (these two are popped up in my head).
How about to create a 2 dimensional grid segmentation based on the number of questions, the sizes of the question panel and an array holding the position of each question coordinates and then on each time frame to position randomly these panels on one of the allowed coordinates.
Note: read this article for further information: http://eloquentjavascript.net/chapter8.html
The second approach follow the same principle, but this time to check if the panel overlap the existing panel before you place it on the canvas.
var _grids;
var GRID_SIZE = 20 //a constant holding the panel size;
function createGrids() {
_grids = new Array();
for (var i = 0; i< stage.stageWidth / GRID_SIZE; i++) {
_grids[i] = new Array();
for (var j = 0; j< stage.stageHeight / GRID_SIZE; j++) {
_grids[i][j] = new Array();
}
}
}
Then on a separate function to create the collision check. I've created a gist for collision check in Actionscript, but you can use the same principle in Javascript too. I've created this gist for inspirational purposes.
Just use a random number which is based on the width of your board and then modulo with the height...
You get a cell which is where you can put the mole.
For the positions the x and y should never change as you have 9 spots lets say where the mole could pop up.
x x x
x x x
x x x
Each cell would be sized based on % rather then pixels and would allow re sizing the screen
1%3 = 1 (x)
3%3 = 0 (y)
Then no overlap is possible.
Once the mole is positioned it can be show or hidden or moved etc based on some extended logic if required.
If want to keep things your way and you just need a quick re-position algorithm... just set the NE to the SW if the X + width >= x of the character you want to check by setting the x = y+height of the item which overlaps. You could also enforce that logic in the drawing routine by caching the last x and ensuring the random number was not < last + width of the item.
newY = randomFromTo(minY, maxY);
newX = randomFromTo(minX, maxX); if(newX > lastX + characterWidth){ /*needful*/}
There could still however be overlap...
If you wanted to totally eliminate it you would need to keep track of state such as where each x was and then iterate that list to find a new position or position them first and then all them to move about randomly without intersecting which would would be able to control with just padding from that point.
Overall I think it would be easier to just keep X starting at 0 and then and then increment until you are at a X + character width > greater then the width of the board. Then just increase Y by character height and Set X = 0 or character width or some other offset.
newX = 0; newX += characterWidth; if(newX + chracterWidth > boardWidth) newX=0; newY+= characterHeight;
That results in no overlap and having nothing to iterate or keep track of additional to what you do now, the only downside is the pattern of the displayed characters being 'checker board style' or right next to each other (with possible random spacing in between horizontal and vertical placement e.g. you could adjust the padding randomly if you wanted too)
It's the whole random thing in the first place that adds the complexity.
AND I updated your fiddle to prove I eliminated the random and stopped the overlap :)
http://jsfiddle.net/pUwKb/51/
If for instance, we set a -vendor-transform: rotate(40deg) css attribute on a rectangle <div>, all the sudden dragging and resizing becomes very weird and flawed.
Here is an example with a simple jQueryUI: http://jsfiddle.net/Ja4dY/1/
You will notice, that if you drag or resize that rectangle when transformed, it will jump up or down and the cursor will not remain in the correct place. In my real code I'm using custom code for resizing and dragging, however I encountered the same problems.
Well, of course the "problem" is that the direction of an Element will change. So left can be right, top gets bottom and something inbetween and the Javascript code still handles each direction as it would be not transformed.
So, the question: How can we compensate transformed / rotated Elements ?
Any good resources / books / blogs are also very welcome.
You can get the current transformation matrix that is applied to an element by using getComputedStyle(). You can use this to transform the current mouse position to its position in transformed space and see whether the click/drag events are within the element boundary and/or corners. Good resources for this:
http://www.useragentman.com/blog/2011/01/07/css3-matrix-transform-for-the-mathematically-challenged/
http://www.eleqtriq.com/2010/05/css-3d-matrix-transformations/
BTW, as you're experiencing, this is non-trivial to code. We had to do it for Sencha Animator, and it was a beast.
The problem is that functions that make elements draggable, wether using jQuery UI or not, relies heavily on the native getBoundingClientRect() function to figure out the position of the element etc.
When applying CSS3 transforms, like rotation, the values of getBoundingClientRect() or the equalent jQuery offset() function used in jQuery UI no longer works as expected, and the position of the mouse pointer gets messed up because the size of the element is suddenly wrong after it has been rotated.
To fix it you need to add some sort of helper function that recalculates the values, and there is a monkey patch available for this that works with jQuery UI's draggable.
It's hard to say anything about how to make the same patch work for custom code, but you'll probably have to integrate it in your custom function somehow, and it will take some coding on your part, and it's even harder to come up with something that works as a helper function out of the box for custom code one has not seen, and be aware that it is rather involved doing these calculations, see the code below :
function monkeyPatch_mouseStart() {
var oldFn = $.ui.draggable.prototype._mouseStart ;
$.ui.draggable.prototype._mouseStart = function(event) {
var o = this.options;
function getViewOffset(node) {
var x = 0, y = 0, win = node.ownerDocument.defaultView || window;
if (node) addOffset(node);
return { left: x, top: y };
function getStyle(node) {
return node.currentStyle || // IE
win.getComputedStyle(node, '');
}
function addOffset(node) {
var p = node.offsetParent, style, X, Y;
x += parseInt(node.offsetLeft, 10) || 0;
y += parseInt(node.offsetTop, 10) || 0;
if (p) {
x -= parseInt(p.scrollLeft, 10) || 0;
y -= parseInt(p.scrollTop, 10) || 0;
if (p.nodeType == 1) {
var parentStyle = getStyle(p)
, localName = p.localName
, parent = node.parentNode;
if (parentStyle.position != 'static') {
x += parseInt(parentStyle.borderLeftWidth, 10) || 0;
y += parseInt(parentStyle.borderTopWidth, 10) || 0;
if (localName == 'TABLE') {
x += parseInt(parentStyle.paddingLeft, 10) || 0;
y += parseInt(parentStyle.paddingTop, 10) || 0;
}
else if (localName == 'BODY') {
style = getStyle(node);
x += parseInt(style.marginLeft, 10) || 0;
y += parseInt(style.marginTop, 10) || 0;
}
}
else if (localName == 'BODY') {
x += parseInt(parentStyle.borderLeftWidth, 10) || 0;
y += parseInt(parentStyle.borderTopWidth, 10) || 0;
}
while (p != parent) {
x -= parseInt(parent.scrollLeft, 10) || 0;
y -= parseInt(parent.scrollTop, 10) || 0;
parent = parent.parentNode;
}
addOffset(p);
}
}
else {
if (node.localName == 'BODY') {
style = getStyle(node);
x += parseInt(style.borderLeftWidth, 10) || 0;
y += parseInt(style.borderTopWidth, 10) || 0;
var htmlStyle = getStyle(node.parentNode);
x -= parseInt(htmlStyle.paddingLeft, 10) || 0;
y -= parseInt(htmlStyle.paddingTop, 10) || 0;
}
if ((X = node.scrollLeft)) x += parseInt(X, 10) || 0;
if ((Y = node.scrollTop)) y += parseInt(Y, 10) || 0;
}
}
}
this.helper = this._createHelper(event);
this._cacheHelperProportions();
if($.ui.ddmanager)
$.ui.ddmanager.current = this;
this._cacheMargins();
this.cssPosition = this.helper.css("position");
this.scrollParent = this.helper.scrollParent();
this.offset = this.positionAbs = getViewOffset(this.element[0]);
this.offset = {
top: this.offset.top - this.margins.top,
left: this.offset.left - this.margins.left
};
$.extend(this.offset, {
click: {
left: event.pageX - this.offset.left,
top: event.pageY - this.offset.top
},
parent: this._getParentOffset(),
relative: this._getRelativeOffset()
});
this.originalPosition = this.position = this._generatePosition(event);
this.originalPageX = event.pageX;
this.originalPageY = event.pageY;
(o.cursorAt && this._adjustOffsetFromHelper(o.cursorAt));
if(o.containment)
this._setContainment();
if(this._trigger("start", event) === false) {
this._clear();
return false;
}
this._cacheHelperProportions();
if ($.ui.ddmanager && !o.dropBehaviour)
$.ui.ddmanager.prepareOffsets(this, event);
this.helper.addClass("ui-draggable-dragging");
this._mouseDrag(event, true);
if ( $.ui.ddmanager ) $.ui.ddmanager.dragStart(this, event);
return true;
};
}
monkeyPatch_mouseStart();
And here's a FIDDLE showing it working as expected with jQuery UI's draggable and resizeable !
I found this... It's a working example plus info, demo and download link.
jquery-ui-rotation-using-css-transform -> live-demo
He use his own libraries, but if you are interest in the subject, you can read and learn how he get it.
cheers and good luck.
Gmo.-
Btw, the web is in Russian, but with google translate you can manage ;-)
It is not bug in jQuery. Simply it is not supported. If you check jQuery UI source code you will figure out that it doesn't use transformation matrix to calculate difference between transformed object and page.
Your example, and probably every jQ UI drag implementation suffer from this issue cause of 2 methods in JQ UI source code (around 314 line of jquery.ui.draggable.js file v1.8.23 ). Calculated offset do not matter about change in offset since rotation is done over center of element.
You have to calculate what is that change. Here is workaround, quick and dirty. The idea is to check what is difference in bounding box of transformed element.
Check sample here http://jsfiddle.net/mjaric/9Nqrh/
Ignore part with first two rotations, they are just done to minimize lines of code. Third involves translation of coordinate system for calculated difference. It will offset left and top after translation is performed (note it is first in filter).
If you want to avoid first two rotation filters, You could make code using formula for 2D rotation:
x' = x cos f - y sin f
y' = y cos f + x sin f
where f is angle of rotation, but it's not that simple and also includes more lines of code where you have to calculate what is diagonal angle of original bounding box since you need initial angle of top left corner which x and y coords are comparing to x axis (positive part). Then calculate change in x-x' and y-y'. But I'm predicting some issues with sign of change and coding/debugging would take more time then I have right now. Sorry cause of that but I'm sure you can figure out what to do after reading this post.
It looks better if we override the cursorAt:
$("#foo").mousedown(function (e) {
var x = e.pageX - this.offsetLeft;
var y = e.pageY - this.offsetTop;
console.log(x);
$("#foo").draggable("option", "cursorAt", {left: x, top:y});
});
Updated fiddle: http://jsfiddle.net/johnkoer/Ja4dY/8/
You said you are not interested with JQuery solutions then,
One solution is;
I recommend you to write your own drag and resize functions. You can
handle resizing and draging on rotated objects to add their top and left with sine and cosine of that degree.
Another solution is;
You can use libraries like Raphael JS to create objects to transform,
drag and resize. Raphael JS uses svg!
For more information about Raphael JS
Yet another solution is;
If you do not want to use library like Raphael JS, you can directly use SVG with JQuery
For more information about SVG
Cannot write more details now, I expand this solutions tomorrow.
Hope these help for now.
This, indeed, seems to be a bug in jQuery. An easy workaround would be: surround the resizable div with a container div. Set the .draggable() to the outer div and .resizable() to the inner div. This seems to be working fine in Chromium running on Ubuntu. See Fiddle.
I've colored the outer div to give you an idea what's happening under the hood.
I've been trying to make a javascript to get a X and Y coordinates of a div element. After some trying around I have come up with some numbers but I'm not sure how to validate the exact location of them(the script returns the X as 168 and Y as 258) I'm running the script with a screen resolution of 1280 x 800. This is the script I use to get this result:
function get_x(div) {
var getY;
var element = document.getElementById("" + div).offsetHeight;
var get_center_screen = screen.width / 2;
document.getElementById("span_x").innerHTML = element;
return getX;
}
function get_y(div) {
var getY;
var element = document.getElementById("" + div).offsetWidth;
var get_center_screen = screen.height / 2;
document.getElementById("span_y").innerHTML = element;
return getY;
}
Now the question is. Would it be reasonable to assume that these are accurate coordinates returned by the function or is there an easy to to just spawn a little something on that location to see what exactly it is?
And finally how would I go about making this div element move? I know I should use a mousedown event handler and a while to keep moving the element but yeah any tips/hints are greatly appreciated my biggest concern is to how to get that while loop running.
By far, the easiest way to get the absolute screen position of an element is getBoundingClientRect.
var element = document.getElementById('some-id');
var position = element.getBoundingClientRect();
var x = position.left;
var y = position.top;
// Et voilà!
Keep in mind, though, that the coordinates don’t include the document scroll offset.
Here a simple way to get various information regarding the position of a html element:
var my_div = document.getElementById('my_div_id');
var box = { left: 0, top: 0 };
try {
box = my_div.getBoundingClientRect();
}
catch(e)
{}
var doc = document,
docElem = doc.documentElement,
body = document.body,
win = window,
clientTop = docElem.clientTop || body.clientTop || 0,
clientLeft = docElem.clientLeft || body.clientLeft || 0,
scrollTop = win.pageYOffset || jQuery.support.boxModel && docElem.scrollTop || body.scrollTop,
scrollLeft = win.pageXOffset || jQuery.support.boxModel && docElem.scrollLeft || body.scrollLeft,
top = box.top + scrollTop - clientTop,
left = box.left + scrollLeft - clientLeft;
You need to find the position using the parent's position too. There's a very good tutorial here: http://www.quirksmode.org/js/findpos.html
I think you could use jQuery .offset() http://api.jquery.com/offset/
Given the element...
<div id="abc" style="position:absolute; top:350px; left:190px;">Some text</div>
If the element is in the main document you can get the DIV's coordinates with...
var X=window.getComputedStyle(abc,null).getPropertyValue('left');
var Y=window.getComputedStyle(abc,null).getPropertyValue('top');
If the element is in an iframe you can get the DIV's coordinates with...
var X=FrameID.contentWindow.getComputedStyle(abc,null).getPropertyValue('left');
var Y=FrameID.contentWindow.getComputedStyle(abc,null).getPropertyValue('top');
NB: The returned values should be in the format "190px" and "350px".