So i need to create a basic bar graph from scratch using javascript, i tried setting some things such as, I attempted to start trying to define some values but it all went wrong.
The task is to create a program that will accept some of this data, input by the user, and to produce a graph that is suitably formatted.
The output could be made to look like this:
Here is the data that is provided
How would I simply even attempt this from scratch?
Given all the complexities of charting, I think most people just use one of the many open source charting libraries. SparkLines is just one example.
Yet, sometimes you just want a simple chart without adding a library. And a bar chart is one of the easiest to build. The code snippet below is very basic, but it is enough to get OP started with the homework assignment.
Run code snippet to view:
<html>
<body>
<style type="text/css">
#chart {background-color: lightyellow; position: relative; height:200px; width: 200px; border: 1px black solid; display: table-cell; vertical-align: bottom; font-size: 10px; }
.bar {position: absolute; bottom: 0; display:inline-block; width: 10px; margin: 2px; background-color: lightpink;}
</style>
<div id="chart"></div>
<script type="text/javascript">
var i, max, min, h, html='', data = [34.7,68.9,65.1,130.2,208.6,172.8,155.0,168.6,134.4,52.7,94.5,41.5];
max = min = data[0];
for(i=0; i<data.length; i++) {
if (max < data[i]) max = data[i];
if (min > data[i]) min = data[i];
}
for(i=0; i< data.length; i++) {
h = Math.round( 100 * ((data[i] - min) / max));
html += '<div class="bar" style="height:' + h + '%; left:' + (12 * i) + 'px">' + data[i] + '</div>';
}
document.getElementById('chart').innerHTML = html;
</script>
</body>
</html>
Group the values on the X axis so theres the number of groups left that you want on the X axis
Calculate the average of Y for each group
Get the maximum value of all your groups; say it's 320
Take a height in pixels that you want your graph to have; say 200
Calculate the height in pixels that each group has with the rule of three:
320 = 200
pixel-y = group-y
Paint the respective group on your graph
For example, if a group has a Y value of 120:
320 = 200
pixel-y = 120
pixel-y = (320 * 120) / 200
pixel-y = 192
Related
I want to arrange some rectangular div components around a regular polygon. Basically one of the long sides of the divs will be coincident with a line segment around the polygon.
In the final code, I'll use .ejs (since the number of sides of the polygon is dynamic, 3-10 sides). In my "quick and dirty" testing I'm doing a triangle in just HTML and CSS to get the math right.
I have a "very close" solution already and am wondering how to get it "exact" and am also wondering why my geometry intuition is so far off.
HTML and CSS:
div {
position: absolute;
left: 200px;
top: 200px;
width: 80px;
height: 40px;
background-color: skyblue;
}
.rotatedA {
transform: translateY(-60px) translateX(-35px) rotate(300deg);
background-color: blue;
}
.rotatedB {
transform: translateY(-60px) translateX(35px) rotate(60deg);
background-color: red;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>title</title>
<link rel="stylesheet" href="basic.css">
</head>
<body>
<div>Normal</div>
<div class="rotatedA">Rotated</div>
<div class="rotatedB">Rotated</div>
</body>
</html>
The first attempt I rotated "A" by 60 and "B" by -60 and did a translateY equal to the div height. When that did not work I played around with it.
On this last attempt (close but not perfect since the rotations won't give an integer) it seems like the Y adjustment is 1.5x (item height + cos(60)) but the X adjustment is 1/2 of sin(60) (I don't understand why).
Since my results aren't going to be an integer number of pixels what is the correct way to do this? Also, I don't understand why my geometry is so off (I could understand sin(60) but 1/2(sin(60)) doesn't make sense to me
Here's a mathematical way; the number and dimensions are read by the script, then the divs are arranged accordingly. I also made sure that the wrapper container has the correct dimensions so it can be used with other elements:
function arrange(wrapper) {
wrapper.style.position = "relative";
const rects = Array.from(wrapper.children);
const n = rects.length;
/* dimensions of a rectangle */
const bb = rects[0].getBoundingClientRect();
const a = bb.width;
const h = bb.height;
/* incircle radius of regular polygon */
const r = a * 0.5 / Math.tan(Math.PI / n);
/* radius of outer circle */
const bigR = Math.sqrt((r + h) * (r + h) + a * a / 4);
rects.forEach((rect, i) => {
const angle = i * (360 / n);
if (angle) rect.style.transform = `rotate(${angle}deg)`;
rect.style.position = angle ? "absolute" : "relative";
rect.style.marginBottom = bigR + r + "px";
rect.style.transformOrigin = `${a/2}px ${-r}px`;
rect.style.left = bigR - a / 2 + "px";
rect.style.top = bigR + r + "px";
});
if (window.getComputedStyle(wrapper).display == "inline-block")
wrapper.style.width = 2 * bigR + "px";
}
arrange(document.querySelector('#polygon'));
#polygon {
border: 1px solid black;
display: inline-block;
}
#polygon div {
width: 80px;
height: 20px;
background-color: skyblue;
text-align: center;
padding: 5px;
}
<div id="polygon">
<div>Normal</div>
<div>Rotated</div>
<div>Rotated</div>
<div>Rotated</div>
<div>Rotated</div>
<div>Rotated</div>
<div>Rotated</div>
</div>
The basic idea is to
calculate the in-circle's radius of the polygon based on the width of a rectangle
set transform-origin accordingly centered and above the first rectangle
arrange the others by rotating them
(do more calculations so the wrapper element encompasses everything exactly)
Hi I am using a hashmap that allows me to efficiently detect objects in the given coordinates. However it is working perfectly , the problem lies with using the mouse to gather the position of the mouse within the canvas down to the pixel. I have been using the offsetX and offsetY methods for the event to gather some offset but it seems there is an offset I am unaware of and may have something to do with either:
1.using scaling on the canvas , Note: ive tried to fix this by division of the renderscale, this works with everything else so should be fine here.
mouseoffset is not accounting for parts of the page or is missing pixels at a low level (maybe 20) but divided by the render scale thats massive.
3.I am using a cartesian coordinate system to simplify things for the future , so the game map is in cartesian and may have to do with the problem.
I will not be supplying all the code because it is allot of work to go through it all so i will supply the following :
the html/css canvas code
<html lang="en">
<head>
<meta charset="UTF-8">
<title> Game</title>
</head>
<body onload="jsEngine = new JsEngine(24, 24, .1); " >
<div class ="wrapper">
<canvas id="canvas" width="1920" height="1080"></canvas>
</div>
<style>
.wrapper {
position: relative;
width: auto;
height: 900px;
}
.wrapper canvas {
position: absolute;
left: 90px;
top: 50px;
padding-left: 0;
padding-right: 0;
margin-left: auto;
margin-right: auto;
display: block;
width: 90%;
height: 90%;}
.GUI{
top: -315px;
left: -302px;
position: absolute;
width: 300px;
height: 300px;
background-color: cadetblue;
opacity: .5;
word-wrap: break-word;}
img{
image-rendering: optimize-contrast;
}
</style>
<div id = GUI class = "GUI"></div>
<!-- Libraries -->
<script src="../myapi/JSONE.js"></script>
<script src="../myapi/engine/SpacialHash.js"></script>
</body>
</html>
2.the javascript click function
//Click on objects
let onClick = function(event){
let canvas_ctx = document.getElementById("canvas").getContext("2d");
let canvasOffsetX = canvas_ctx.canvas.width/2;
let canvasOffsetY = canvas_ctx.canvas.height/2;
let mousePosX = event.clientX;
let mousePosY = event.clientY;
let mouseX =jsEngine.cameraFocus.x-canvasOffsetX/jsEngine.renderScale+(mousePosX)/jsEngine.renderScale;
let mouseY = jsEngine.cameraFocus.y+(canvasOffsetY)/jsEngine.renderScale+((-mousePosY)/jsEngine.renderScale);
console.log("sum to",mouseX,mouseY);
//My hashMap to place the mouse coordinates on the game map
let clickPosition = hm.find({x:mouseX,y:mouseY,width:1,height:1});
if(clickPosition.length===1){
let gameObject = jsEngine.gameObjects[clickPosition[0].range.id];
//console.log(gameObject.transform.x,gameObject.transform.y,mouseX,mouseY);
let clickBox = {};
let picture = gameObject.texture;
guiCreateClickBox(clickBox,gameObject.id,1200,500,picture);
}else if(clickPosition.length>1) {
for (let i = 0; i < clickPosition.length; i++) {
let gameObject = jsEngine.gameObjects[clickPosition[i].range.id];
if (gameObject instanceof PlayerShip|| gameObject instanceof Bullet)
continue;
let clickBox = {};
let picture = gameObject.texture;
guiCreateClickBox(clickBox,gameObject.id,1200,500,picture);
//console.log(gameObject.transform.x,gameObject.transform.y,mouseX,mouseY)
}
}
};
// Listeners
//Click on objects
document.getElementById("canvas").addEventListener("click", onClick);
the making of the map and scale :Note: this is done via onPreRender
function drawBackground(canvas_ctx, renderScale, imageResource) {
let img = imageResource.mapBackground;
let mapWidth = 1000000;
let mapHeight= 1000000;
let zoom = 1;
mapWidth *= renderScale / zoom;
mapHeight *= renderScale / zoom;
// Render the Background
canvas_ctx.fillStyle = canvas_ctx.createPattern(img, 'repeat');
canvas_ctx.scale(zoom, zoom);
canvas_ctx.fillRect(-mapWidth / 2, - mapHeight / 2, mapWidth, mapHeight);
//if (jsEngine.cameraFocus.x > 1000000) {}
canvas_ctx.scale(1/zoom, 1/zoom);
}
The rendering method used for playership
renderGameObject(gameObject) {
let x = gameObject.transform.x * this.renderScale;
let y = -(gameObject.transform.y * this.renderScale);
let rotation = Math.radians(gameObject.transform.rotation);
let width = gameObject.transform.width;
width *= this.renderScale;
let height = gameObject.texture.height;
height *= this.renderScale;
// Render the gameObject
this.canvas_ctx.translate(x, y);
this.canvas_ctx.rotate(rotation);
this.canvas_ctx.drawImage(gameObject.texture, 0, 0, width / this.renderScale, height / this.renderScale, // Make sure the image is not cropped
-width/2 , // X
-height/2 , // Y
width, height); // width and height
this.canvas_ctx.rotate(-rotation);
this.canvas_ctx.translate(-x, -y);
}
the issue to solve is to make it so that when you click on any given quadrant of the canvas it will return -+ for top left, -- bottom left , -+ topright, +- bottomright, as well as being applied to the render scale which at the moment is .1 so just divide your mouse and canvas coords like shown above and you should be able to get the same results.
Things to keep in mind :
the jsEngine.cameraFocus is set to the playerships x and y coordinates(which are set to the 0,0 posiiton on the map) (which are also in the middle of the ship)
the top left of the canvas is still 0,0 and ++ is still toward the bottom right so theoretically minusing half the canvas width/height then adding the offsets X and Y. this should be working but at my map coordinate -4000,-4000 i get ~-3620,-3295 and at +4000,+4000 I get 3500,3500. (The reason why the canvas 0,0 is not where the ship is , is to make the ship in the middle of the screen)
If you have questions about anything based on code that needs to be supplied please ask via comment . Please note if you have problems with the format of the code supplied I have nothing to say about it . all I need is the click function working on the canvas model i set up in cartesian format.
ps: jQuery is not a solution its a problem please use vanilla js.
I found out why it was off , my canvas has a offset of 90 px and 50 px as well as the main problem that the canvas is only 90% of its origonal size (also in css). If anyone can give me help for how to adjust to these issues please reply in comment . until then I beleieve I have solved my own issue .
First of all - please do not remove this post.
It's not a duplicate.
I know it covers a problem that was mentioned here multiple times but this time it's not "how to detect collisions" because as you will see later, it's already done. It's more about "how to write" this in as much optimized way as possible, because below detection will be triggered multiple times in a short delay of time.
Here's my fiddle: http://jsfiddle.net/slick/81y70h1f/
I generate random squares and detect if they collide with each other.
HTML is generated using below way. No rocket science:
<?php for ($i=1; $i<=$amount; $i++) { ?>
<div id="square_<?= $i; ?>" class="square" style="top: <?= rand(0, 800); ?>px; left: <?= rand(0, 800); ?>px;">
<div>square_<?= $i; ?></div>
</div>
<?php } ?>
In the fiddle, $amount is set to 16. As you can imagine, the possible amount of unique pair combination is equal to:
In the fiddle you will see that I perform the uniqueness calculation twice. Second time just for squares that don't collide.
var squares_without_collision = $(squares).not(garbage).get();
pairs_cleaned = get_unique_pairs(squares_without_collision);
The pairs_cleaned is my final array when I will perform the secret operation that is not a part of this problem. This array will be always slightly reduced with unnecessary crap.
When I will increase $amount to 100 I will get 4950 possible combination. When I refresh page it still works fine but I can observe the speed drops down. I even didn't try to set it to 200 because I don't want my browser to crash.
Question - is here still any space of the improvement and optimization? Because now I will reveal that these squares will be Google Map markers and my collision calculation will be triggered on events when:
Tiles are loaded
Map is dragged
Zoom is changed
In the final version, instead of changing background from green to red, I will be showing or hiding markers. I'm worried, that with more markers I will do a turtle script. I would like to keep it extra fast.
Ok had a look and you have way over complicated it. No need to find the pairs, you are querying the DOM way to often. You should only touch the DOM once for each element. The garbage array is redundant use a semaphore. Never use each() in time critical code as it is very slow.
Always keep variables in function scope (inside the main function) because leaving them in global scope will half the access speed.
Arrays are slow and should be avoided at all costs. Reuse array items if you can. Always ask do you really need a new array? is there a way not to use an array?
Dont test where not needed. You have some garbage but you retest those squares.
Avoid function calls inside loops of time critical code. Calling a function is CPU intensive it is way better to have code inline.
Avoid indexing into arrays. Reference the array item once and use the reference.
Avoid JQuery unless you have a clear and justified reason. JQuery is VERY slow and encourages excessive DOM manipulation.
Think that's it. Below is your Fiddle modified that will run a lot faster.
$(function () {
var squares = []; // keep arrays in function scope as out side the function
var pairs_cleaned = []; // they are in global scope and run at half the speed.
var x1,y1;
squares = $('.square'); // get the squares
var len = squares.length;
console.log('----- Squares away ' + len + '------');
console.log(squares);
var width = 80+10; // you can do this get the size and padding from the first square
var height = 80+10; // if each square is a different size then you will have to change the code a little
for(var i = 0; i < len; i += 1){ // itterate them. Avoid using Each in time critical code as it is slow
var div = squares[i];
squares[i] = { // replace the existing array with a new object containing all we will need. This reuses the array and avoids overheads when growing an array.
square:div, // save the square. Not sure if you need it?
garbage:false, // flage as not garbage
x: x1 = Number(div.offsetLeft), // get the squares location
y: y1 = Number(div.offsetTop), // and ensure all values are Numbers
b: y1 + height, // I have only included the static height and width.
r: x1 + width,
};
}
var s1,s2;
for (var i = 0; i < len; i++) { // instead of calling the function to get an array of pairs, just pair them on the fly. this avoid a lot of overhead.
s1 = squares[i]; // reference the item once outside the loop rather than many times inside the next loop
for (var j = i + 1; j < len; j++) {
if(!squares[j].garbage){ // ignore garbage
s2 = squares[j];
// do the test inside the loop rather than call a function. This avoids a lot of overhead
if (s1.x > s2.r || s1.y > s2.b || s1.r < s2.x || s1.b < s2.y){ // do test
pairs_cleaned.push([s1,s2]); // if passed save unique pairs
}else{
s2.square.style.backgroundColor = '#ff0040'; // this should not be here is Very very slowwwwwwwww
s2.garbage = true; // garbage
}
}
}
}
console.log('----- all pairs without garbage ------');
console.log(pairs_cleaned);
});
OK. Hope that helps. It's been run and works on chrome. You will need to look at the querying of the elements for position and size but I did not think it important for this example.
There are other optimizations you can do but this should see you to around 1000 squares in realtime if you get rid of the s2.square.style.backgroundColor = '#ff0040'; from the inner loop. It is the slowest part of the whole collision test loop. DOM is death for fast code requirements. Always keep all DOM contact out of critical code sections.
One last thing. To get the best performance always use strict mode, it will give you 20%+ increased performance on most code.
You may consider implementing a simple collision grid for the task. That is, take a conceptual 2D grid spanning over the whole collision field, where each grid cell has a size greater than or equal to the maximum size of the colliding nodes, and bin the center points of each collision node in a data structure representing the grid.
From there, for each given collision node, you only need to check for collisions against other nodes placed in any of the adjacent grid cells to the current collision node's grid cell.
For example:
Say the width and height of your map is 1000px, and the collision nodes are represented in squares of 50x50 pixels. You choose to implement a 100px by 100px grid.
So you would first create a data structure that consists of a 2D array where each cell holds an array that will store collision objects:
var gridSize = { w: 1000, h: 1000 }; // The predefined grid size
var blockSize = { w: 100, h: 100 }; // The predefined block size
var collisionGrid = [];
// Initialize a grid of blockSize blocks to fill the gridSize
var x, y, gridX, gridY;
for (x = 0; x < gridSize.w; x += blockSize.w) {
gridX = x/blockSize.w;
collisionGrid[gridX] = [];
for (y = 0; y < gridSize.h; y += blockSize.h) {
gridY = x/blockSize.h;
collisionGrid[gridX][gridY] = [];
}
}
Then, as you learn about the locations of collision nodes (fetched data from some API, for instance), you would populate the data structure with references to each of the collision nodes according to where it's center point is placed on the grid.
So a square collision node with { x: 726, y:211, w: 50, h:50 } would be placed like this:
var placeNode = function(node) {
var mid = {
x: node.x + node.w/2,
y: node.y + node.h/2
};
var cell = {
x: Math.floor(mid.x/blockSize.w),
y: Math.floor(mid.y/blockSize.h)
};
collisionGrid[cell.x][cell.y].push(node);
};
var node = { x: 726, y:211, w: 50, h:50 } // ...fetched from some API
placeNode(node);
After a few hundred or thousand nodes are placed in the grid (which takes very little overhead for each - just a division or two and pushing a reference to an array), checking for collisions for a given node is greatly reduced, since you only need to check for collisions against nodes in the current node's cell as well as the 8 adjacent cells.
In this example, nodes only within a 300x300px block will be checked against for a given node, but as the collision field size increases and the grid size/collision node sizes decrease, this technique can really shine.
In this blog post, I lightly explained the implementation of this kind of grid for collision for a game I was working on: http://blog.cheesekeg.com/prototype-just-the-basics-v0-2/
One thing to note is that there is a trade off here - when collision nodes move around, their corresponding references to moved from grid cell to grid cell as they travel over the grid. However, in the post I linked above, this fact doesn't cause any noticeable problems with performance when hundreds of collision nodes are moving about the grid.
As Brandon mentioned you are best to create some kind of grid to decrease the number of collisions that you actually detect.
I would suggest using plain javascript rather than jQuery for this if you really want the most performance but here is a jQuery solution I created.
var gridDimensions = {
x: 800,
y: 800
};
var boxDimensions = {
x: 80,
y: 80
};
var hashes = hashSquares($('.square'), gridDimensions, boxDimensions);
function hashSquares($squares, dimensions, squaresDimensions) {
var squaresHash = [];
for (var i = 0; i < Math.floor(dimensions.x / squaresDimensions.x); i++) {
var yHashes = Array(Math.floor(dimensions.y / squaresDimensions.y));
for (var j = 0; j < yHashes.length; j++) {
yHashes[j] = [];
}
squaresHash.push(yHashes);
}
$squares.each(function() {
var $this = $(this);
squaresHash[Math.floor($this.position().left / squaresDimensions.x)][Math.floor($this.position().top / squaresDimensions.y)].push($this);
});
return squaresHash;
}
function checkSameSquare(x, y, hash) {
//if they are both in the same hash square they definitely overlap
if (hash[x][y].length > 1) {
$.each(hash[x][y], function(i, $el) {
//skip the first element
if (i !== 0) {
$el.addClass('collided');
}
});
}
}
function checkSquareBelow(x, y, hash) {
$.each(hash[x][y], function(i, $el) {
$.each(hash[x][y + 1], function(i2, $el2) {
if (detectCollision($el, $el2)) {
$el2.addClass('collided');
}
});
});
}
function checkSquareRight(x, y, hash) {
$.each(hash[x][y], function(i, $el) {
$.each(hash[x + 1][y], function(i2, $el2) {
if (detectCollision($el, $el2)) {
$el2.addClass('collided');
}
});
});
}
function checkSquareDiagonalRightBelow(x, y, hash) {
$.each(hash[x][y], function(i, $el) {
$.each(hash[x + 1][y + 1], function(i2, $el2) {
if (detectCollision($el, $el2)) {
$el2.addClass('collided');
}
});
});
}
function detectCollision($div1, $div2) {
var x1 = $div1.offset().left;
var y1 = $div1.offset().top;
var h1 = $div1.outerHeight(true);
var w1 = $div1.outerWidth(true);
var b1 = y1 + h1;
var r1 = x1 + w1;
var x2 = $div2.offset().left;
var y2 = $div2.offset().top;
var h2 = $div2.outerHeight(true);
var w2 = $div2.outerWidth(true);
var b2 = y2 + h2;
var r2 = x2 + w2;
if (b1 < y2 || y1 > b2 || r1 < x2 || x1 > r2) return false;
return true;
}
for (var i = 0; i < hashes.length; i++) {
for (var j = 0; j < hashes[i].length; j++) {
checkSameSquare(j, i, hashes);
if (j < hashes[i].length - 1) {
checkSquareRight(j, i, hashes);
}
if (i < hashes.length - 1) {
checkSquareBelow(j, i, hashes);
}
if (j < hashes[i].length - 1 && i < hashes.length - 1) {
checkSquareDiagonalRightBelow(j, i, hashes);
}
}
}
body {
margin: 10px;
font-family: Arial, sans-serif;
}
#container {
background-color: #cccccc;
height: 880px;
position: relative;
width: 880px;
}
.square {
background-color: lawngreen;
height: 80px;
position: absolute;
width: 80px;
z-index: 10;
}
.square > div {
font-size: 12px;
padding: 5px;
}
.square:hover {
background-color: forestgreen;
z-index: 11;
cursor: pointer;
}
.collided {
background-color: red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="container">
<div id="square_1" class="square" style="top: 31px; left: 141px;">
<div>square_1</div>
</div>
<div id="square_2" class="square" style="top: 56px; left: 726px;">
<div>square_2</div>
</div>
<div id="square_3" class="square" style="top: 555px; left: 391px;">
<div>square_3</div>
</div>
<div id="square_4" class="square" style="top: 725px; left: 330px;">
<div>square_4</div>
</div>
<div id="square_5" class="square" style="top: 398px; left: 642px;">
<div>square_5</div>
</div>
<div id="square_6" class="square" style="top: 642px; left: 794px;">
<div>square_6</div>
</div>
<div id="square_7" class="square" style="top: 521px; left: 187px;">
<div>square_7</div>
</div>
<div id="square_8" class="square" style="top: 621px; left: 455px;">
<div>square_8</div>
</div>
<div id="square_9" class="square" style="top: 31px; left: 549px;">
<div>square_9</div>
</div>
<div id="square_10" class="square" style="top: 677px; left: 565px;">
<div>square_10</div>
</div>
<div id="square_11" class="square" style="top: 367px; left: 120px;">
<div>square_11</div>
</div>
<div id="square_12" class="square" style="top: 536px; left: 627px;">
<div>square_12</div>
</div>
<div id="square_13" class="square" style="top: 691px; left: 312px;">
<div>square_13</div>
</div>
<div id="square_14" class="square" style="top: 93px; left: 757px;">
<div>square_14</div>
</div>
<div id="square_15" class="square" style="top: 507px; left: 720px;">
<div>square_15</div>
</div>
<div id="square_16" class="square" style="top: 251px; left: 539px;">
<div>square_16</div>
</div>
</div>
http://jsfiddle.net/81y70h1f/13/
Notice you only have to test collisions against the same square the square immediately to the right to the bottom right and below as all other collisions are already handled as you move along the grid.
I want to get real X and Y position of my styled elements in javascript ( or jquery short code).
var offset = obj.offset();
ox=offset['left'];
oy=offset['top'];
px=parseInt(obj.css('padding-left')); // padding left
py=parseInt(obj.css('padding-top')); // padding top
bx=parseInt(obj.css('border-width') ); // stroke value
ox=ox+px+bx;
oy=oy+py+bx;
But this codes sometimes not work..
when scrool top or scroll left change im not get real position :(
please help me..
You don't have to use offsets. Use the modern getBoundingClientRect function:
function getPosition( element ) {
var rect = element.getBoundingClientRect();
return {
x: rect.left,
y: rect.top
};
}
You could then use the above function like this:
var element = document.getElementById( 'myElement' );
var pos = getPosition( el );
// Alert position in X axis
alert( pos.x );
// Alert position in Y axis
alert( pos.y );
Works in all browsers 🙂
EDIT: If you want the position of the element with respect to page scroll, just add document.body.scrollTop to Y position and document.body.scrollLeft to X position.
You'll want the offset from the element relative to the document. You have to keep in mind that styles like padding, margin and border can greatly affect the result of the offset. You might want to calculate those on or off the offset.
Finally you need to check if you are using box-sizing, which pushing padding and borders to the inside (with the most common version box-sizing: border-box;);
document.getElementById('cloud').offsetLeft; //offsetTop
Just debug with the other styles (adding/subtracting) until you get the real offset. I mostly test if the offset is correct by making a screenshot (or using the OS X selective-screenshot function) to see if the offset is correctly calculated (counting pixels) with the other styles.
Here's a little example:
CSS
#cloud {
height: 500px;
width: 500px;
margin: 20px auto;
border: 1px dotted #CCC;
}
HTML
<body>
<div id="cloud">
<div id="centerBox"></div>
</div>
</body>
JavaScript
'use strict';
console.log(document.getElementById('cloud').offsetLeft);
console.log(document.getElementById('cloud').offsetTop);
Ouput
main.js: 1 >>> 372
main.js: 2 >>> 20
Funny thing here, you can easily test if the offsetLeft works because of the margin: 20px auto;. If you resize the window it will also have a different offsetLeft.
This function gives you the exact position of an element without padding or margin or border:
function getElementRec(element) {
let rec = { x: 0, y: 0, width: 0, height: 0 };
let computedStyle = getComputedStyle(element);
rec.width = element.clientWidth;
rec.width -= parseFloat(computedStyle.paddingLeft);
rec.width -= parseFloat(computedStyle.paddingRight);
rec.height = element.clientHeight;
rec.height -= parseFloat(computedStyle.paddingTop);
rec.height -= parseFloat(computedStyle.paddingBottom);
let boundingRect = element.getBoundingClientRect();
rec.x = boundingRect.left;
rec.x += parseFloat(computedStyle.paddingLeft);
rec.x += parseFloat(computedStyle.borderLeft);
rec.y = boundingRect.top;
rec.y += parseFloat(computedStyle.paddingTop);
rec.y += parseFloat(computedStyle.borderTop);
return rec;
}
var offset = obj.offset();
ox=offset['left']; = document.getElementById('Mypicture').offsetLeft;
oy=offset['top']; = document.getElementById('Mypicture').offsetTop;
px=parseInt(obj.css('padding-left')); // padding left
py=parseInt(obj.css('padding-top')); // padding top
bx=parseInt(obj.css('border-width') ); // stroke value
ox=ox+px+bx;
oy=oy+py+bx;
//Two codes get equal value; But Not Real clientX ClientY positions..
// My html:
<div id="container">
<img id="Mypicture" src="myimage.jpg" alt="The source image for paint"/>
</div>
//My css :
#Mypicture{
margin:100px;
padding:20px;
border: 20px solid #cacaca;
}
#container {
display:block;
background:#ffaaff;
width: 550px;
padding:50px;
margin-left:300px;
}
// I want to get realx and realY value of Mypicture,on Document.
// All codes work Realx ( ox ) return true value But RealY (oy) nearly 10px false;
How can I find the absolute position of the paper/canvas when using the Raphael JavaScript library?
For instance, suppose the minimal working example is as follows:
<html>
<head>
<script type="text/javascript" src="raphael.js"></script>
<script>
window.onload = function() {
var size = 600;
var paper = new Raphael(document.getElementById("canvas_container"),
size, size);
var c = paper.circle(100, 100, 50).attr({fill: "#00f"});
var x = 0; // get the paper's absolute x coordinate
var y = 0; // get the paper's absolute y coordinate
document.getElementById("coordinates").innerHTML = x + " " + y;
};
</script>
<style type="text/css">
#canvas_container {
width: 600px;
margin-left: auto;
margin-right: auto;
border: 1px solid #aaa;
}
</style>
</head>
<body>
<p id="coordinates"></p>
<div id="canvas_container"></div>
</body>
</html>
How do I find the absolute x and y coordinates of the paper from the top of the page without knowing the ID of the div that it lives in? (In the example, I can know that, but my actual situation makes knowing the div ID much more complicated.)
OK. I see what it should be now:
var x = this.paper.canvas.offsetLeft;
var y = this.paper.canvas.offsetTop;
That seems to work correctly on both IE 8.0 and Chrome 9.0.
To get the abs position of your Raphael paper, you can use jQuery:
$(paper.canvas).offset()
The accepted solution doesn't worked for me, it gives me x = -1 and y = -1, so if someone has the same problem I solved it this way:
var x = paper.canvas.parentNode.offsetLeft;
var y = paper.canvas.parentNode.offsetTop;