getting the X and Y coordinates for a div element - javascript

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".

Related

Get percent scrolled through an element jquery

I can't seem to figure out how to calculate this value
While scrolling down a page, I would like to make a function that return the percertage of 'scrolled through'.
So my 0 would be when the element is on the cusp of being shown (it's at the bottom of the window, 0 pixels shown) and 100 being when the element is completely passed (the element is over the top of the window, 0 pixels shown).
I would need that to do parralax with custom animations, to make an animation that starts when the element is shown, and animates through until the element is gone.
EDIT: All Paralax plugins I see seem to force you in premade animations. I'd like to animate my own thing, so thats why I need that percent value.
After asking the question, I didn't just sit on my hands and continued working on this. This seems to do what I wish to do.
function ScrollPercent(jQEl){
var currY = $('html').scrollTop();
var elH = $(jQEl).height();
var elTop = $(jQEl).offset();
elTop = elTop.top;
var fullH = $('html').height();
var zero = elTop-elH;
var hundred = elTop+$( window ).height();
var scrollPercent = (currY-zero)/(hundred-zero);
return scrollPercent;
}
I've never used jquery. But I can help you with this function.
function ScrollPercent(selector) {
var currY = document.documentElement.scrollTop;
var elH = document.querySelector(selector).offsetHeight;
var elTop = document.querySelector(selector).offsetTop;
var fullH = document.documentElement.scrollHeight;
var zero = elTop - elH;
var hundred = elTop + window.innerHeight;
var scrollPercent = (currY - zero) / (hundred - zero);
return scrollPercent;
}

Dragging & Resizing CSS Transformed Elements

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.

Custom cursor image with javascript

I need to change my mouse cursor to a custom image.
If possible I would like to do it on a spritesheet.
I can't do it from the css because I'm using it in a game. I already know how to decide when etc.
What I need to know is how do I change the cursor to an image, and deciding the image position and size?
Is there any easy solution similar to the drawImage's image position?
You can set the CSS using javascript to hide the cursor:
your_canvas.style.cursor = "none"
You can then get the cursor's position (it's now hidden) with something like this:
your_canvas.addEventListener("mousemove", function (ev) {
var mouseX = ev.pageX - GetTopLeft(your_canvas).Left;
var mouseY = ev.pageX - GetTopLeft(your_canvas).Top;
});
Then you can modify your canvas to show your fancier cursor sprite at that location.
GetTopLeft is defined as follows:
function GetTopLeft(elm){
var x, y = 0;
//set x to elm’s offsetLeft
x = elm.offsetLeft;
//set y to elm’s offsetTop
y = elm.offsetTop;
//set elm to its offsetParent
elm = elm.offsetParent;
//use while loop to check if elm is null
// if not then add current elm’s offsetLeft to x
//offsetTop to y and set elm to its offsetParent
while(elm != null)
{
x = parseInt(x) + parseInt(elm.offsetLeft);
y = parseInt(y) + parseInt(elm.offsetTop);
elm = elm.offsetParent;
}
//here is interesting thing
//it return Object with two properties
//Top and Left
return {Top:y, Left: x};
}
Though I can't remember where I copied the GetTopLeft function from...
If you are using a canvas, just hide the cursor over the canvas and draw your own sprite on the canvas at mouse position.

Javascript scrollIntoView() middle alignment?

Javascript .scrollIntoView(boolean) provide only two alignment option.
top
bottom
What if I want to scroll the view such that. I want to bring particular element somewhere in middle of the page?
try this :
document.getElementById('myID').scrollIntoView({
behavior: 'auto',
block: 'center',
inline: 'center'
});
refer here for more information and options : https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollIntoView
It is possible to use getBoundingClientRect() to get all the information you need to achieve this. For example, you could do something like this:
const element = document.getElementById('middle');
const elementRect = element.getBoundingClientRect();
const absoluteElementTop = elementRect.top + window.pageYOffset;
const middle = absoluteElementTop - (window.innerHeight / 2);
window.scrollTo(0, middle);
Demo: http://jsfiddle.net/cxe73c22/
This solution is more efficient than walking up parent chain, as in the accepted answer, and doesn't involve polluting the global scope by extending prototype (generally considered bad practice in javascript).
The getBoundingClientRect() method is supported in all modern browsers.
Use window.scrollTo() for this. Get the top of the element you want to move to, and subtract one half the window height.
Demo: http://jsfiddle.net/ThinkingStiff/MJ69d/
Element.prototype.documentOffsetTop = function () {
return this.offsetTop + ( this.offsetParent ? this.offsetParent.documentOffsetTop() : 0 );
};
var top = document.getElementById( 'middle' ).documentOffsetTop() - ( window.innerHeight / 2 );
window.scrollTo( 0, top );
document.getElementById("id").scrollIntoView({block: "center"});
Scrolling to the middle of an element works well if its parent element has the css: overflow: scroll;
If it's a vertical list, you can use document.getElementById("id").scrollIntoView({block: "center"}); and it will scroll your selected element to the vertical middle of the parent element.
Cheers to Gregory R. and Hakuna for their good answers.
Further Reading:
https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollIntoView
https://developer.mozilla.org/en-US/docs/Web/CSS/overflow
You can do it in two steps :
myElement.scrollIntoView(true);
var viewportH = Math.max(document.documentElement.clientHeight, window.innerHeight || 0);
window.scrollBy(0, -viewportH/2); // Adjust scrolling with a negative value here
You can add the height of the element if you want to center it globaly, and not center its top :
myElement.scrollIntoView(true);
var viewportH = Math.max(document.documentElement.clientHeight, window.innerHeight || 0);
window.scrollBy(0, (myElement.getBoundingClientRect().height-viewportH)/2);
With JQuery I use this:
function scrollToMiddle(id) {
var elem_position = $(id).offset().top;
var window_height = $(window).height();
var y = elem_position - window_height/2;
window.scrollTo(0,y);
}
Example:
<div id="elemento1">Contenido</div>
<script>
scrollToMiddle("#elemento1");
</script>
Improving the answer of #Rohan Orton to work for vertical and horizontal scroll.
The Element.getBoundingClientRect() method returns the size of an element and its position relative to the viewport.
var ele = $x("//a[.='Ask Question']");
console.log( ele );
scrollIntoView( ele[0] );
function scrollIntoView( element ) {
var innerHeight_Half = (window.innerHeight >> 1); // Int value
// = (window.innerHeight / 2); // Float value
console.log('innerHeight_Half : '+ innerHeight_Half);
var elementRect = element.getBoundingClientRect();
window.scrollBy( (elementRect.left >> 1), elementRect.top - innerHeight_Half);
}
Using Bitwise operator right shift to get int value after dividing.
console.log( 25 / 2 ); // 12.5
console.log( 25 >> 1 ); // 12
None of the solutions on this page work when a container other than the window/document is scrolled. The getBoundingClientRect approach fails with absolute positioned elements.
In that case we need to determine the scrollable parent first and scroll it instead of the window. Here is a solution that works in all current browser versions and should even work with IE8 and friends. The trick is to scroll the element to the top of the container, so that we know exactly where it is, and then subtract half of the screen's height.
function getScrollParent(element, includeHidden, documentObj) {
let style = getComputedStyle(element);
const excludeStaticParent = style.position === 'absolute';
const overflowRegex = includeHidden ? /(auto|scroll|hidden)/ : /(auto|scroll)/;
if (style.position === 'fixed') {
return documentObj.body;
}
let parent = element.parentElement;
while (parent) {
style = getComputedStyle(parent);
if (excludeStaticParent && style.position === 'static') {
continue;
}
if (overflowRegex.test(style.overflow + style.overflowY + style.overflowX)) {
return parent;
}
parent = parent.parentElement;
}
return documentObj.body;
}
function scrollIntoViewCentered(element, windowObj = window, documentObj = document) {
const parentElement = getScrollParent(element, false, documentObj);
const viewportHeight = windowObj.innerHeight || 0;
element.scrollIntoView(true);
parentElement.scrollTop = parentElement.scrollTop - viewportHeight / 2;
// some browsers (like FireFox) sometimes bounce back after scrolling
// re-apply before the user notices.
window.setTimeout(() => {
element.scrollIntoView(true);
parentElement.scrollTop = parentElement.scrollTop - viewportHeight / 2;
}, 0);
}
To support all options in scrollIntoViewOptions for all browsers it's better to use seamless-scroll-polyfill (https://www.npmjs.com/package/seamless-scroll-polyfill)
Worked for me.
Here is a link with explanation https://github.com/Financial-Times/polyfill-library/issues/657

Trigger focus on an element at a given position

I come to you with an intersting question.
Given position (x,y) in an HTML document, how can you trigger a focus event on an element at that given position.
The problem translates into is there any way to select the element matching a given position?
Sort of like getElementByPosition?
The easiest option is to use elementFromPoint:
var element = document.elementFromPoint(x, y);
element.focus();
Other than that, you can write your own function. This is what first came to mind - I used it a while back when there was some reason for elementFromPoint not working correctly, I don't remember what exactly. There are likely to be better ways to do it, but I just tried what I thought of first:
var coords = [100, 100],
elems = document.getElementsByTagName("*");
for(var i = 0; i < elems.length; i++) {
var left = elems[i].offsetLeft,
top = elems[i].offsetTop,
width = elems[i].offsetWidth;
height = elems[i].offsetHeight;
if((left <= coords[0]) && (left + width >= coords[0]) && (top <=coords[1]) && (top + height >= coords[1])) {
elems[i].focus();
}
}
You can see it working here.
you can use elementFromPoint(x, y)
https://developer.mozilla.org/en/DOM/document.elementFromPoint

Categories