This is being weird. I am using getBoundingClientRect() to get the values to position my blue coloured div so that it can surround an element on mouseover.
It works great and is getting all the values correctly. See this for reference:
As you can see, the blue div here surrounded the password element on hover as it should! (I have used Bootstrap demo form from w3schools for this.)
However, the problem arises when I create a form page myself without using Bootstrap or any other framework.
It gets the top and left correctly, but width and height 2px more! See the extra spacing here:
I don't know what is causing this to output those 2px extra. Why it works perfectly with Bootstrap or W3.CSS like frameworks but spit out 2px more without any frameworks? What's the workaround for this?
Here's the code snippet:
document.addEventListener("mouseover", (e) => {
elem = e.target;
var rect = elem.getBoundingClientRect();
var x = rect.left;
var y = rect.top;
var w = rect.width;
var h = rect.height;
div.style.width = w + "px";
div.style.height = h + "px";
div.style.left = x + window.scrollX + "px";
div.style.top = y + window.scrollY + "px";
});
I know how to move a div element using top/left position increments. But I heard that translate3d gives performance improvements, and so I wanted to check it out.
Lets say I have this,
var div = document.createElement('div');
div.style.width = '100px';
div.style.height = '100px';
div.style.background = 'red';
div.style.position = 'absolute';
document.body.appendChild(div);
The following,
div.getBoundingClientRect().left
gives the value 8.
Now, calling
div.style.transform = 'translate3d(10px,0,0)'
moves the element to the right by 10px as expected as seen by
div.getBoundingClientRect().left
giving the value 18.
But repeatedly executing,
div.style.transform = 'translate3d(10px, 0, 0)';
does not move the div to the right repeatedly. Instead I have to increment the first argument of translate3d to higher values (eg. 20, 30 etc) to move it repeatedly.
This makes me think the translation is calculated from some initial point. But googling around, I could not find how to update this initial point, so that translate3d(10px, 0, 0) works in a loop.
I tried updating the div.style.left property, but it still does not work.
So can someone tell me how translate3d calculates the translation? And if there is a way to use translate3d(10px,0,0) and some origin resetting, to make a div move repeatedly?
(The end goal is to make a div move to the right every time it is clicked, using translate3d)
When you are doing div.style.transform = 'translate3d(10px, 0, 0)'; you are just removing the transform value to replace it by another, so your element will keep the same position. If you want to move your element using translate3d, you can use a variable and increment it every click :
var div = document.createElement('div');
div.style.width = '100px';
div.style.height = '100px';
div.style.background = 'red';
div.style.position = 'absolute';
document.body.appendChild(div);
var leftValue = 0;
var increment = 10;
var button = document.querySelector('button');
button.addEventListener('click', function(event) {
leftValue += increment;
div.style.transform = 'translate3d('+leftValue+'px,0,0)';
})
<button>Move</button>
For multiple elements, using getBoundingClientRect().left
When calculating a new offset, we have to take into account the initial position of the element or the new value will be wrong. The problem can be see here.
To avoid this, we can store the initial position of the element and then substract this value when calculating the new offset:
var increment = 10;
var moveBoxes = document.getElementsByClassName('moveBox');
Array.from(moveBoxes).forEach(function(box) {
var initialOffset = getComputedStyle(box).getPropertyValue('left').replace('px', '');
box.addEventListener('click', function(event) {
var offset = this.getBoundingClientRect().left - initialOffset + increment;
this.style.transform = 'translate3d('+offset+'px,0,0)';
});
});
.moveBox {
width: 100px;
height: 100px;
background: red;
position: absolute;
}
<div class="moveBox" style="left: 32px;"></div>
<div class="moveBox" style="top: 120px; left: 100px;"></div>
Using a forEach loop instead of a classical for loop let us store the right initialOffset value in each listeners (there are some others ways to achieve this). Since the array of div is a HTML collection, using Array.from is required to use forEach.
translate3d(10px,0,0)
Is defined here: https://www.w3.org/TR/css-transforms-1/#funcdef-translate3d
I wrote a CodePen to see your issue: https://codepen.io/cyruscuenca/pen/mamjgw?&editable=true
I also scanned the doc, and it seems like the position is based on a coordinate system, and there seems to be separate origin properties. I would read those sections.
The sections that I found that look to be related are 6 and 3.
Hope this helps!
It's very simple.
For the point of the count this function is used your current coordinates by X, Y and Z axes.
So, if you want to move your block, you should always increment this valuse(these values) of the axes.
You can apply multiple transformations on the same element.
div.style.transform = 'translate3d(10px,0,0) translate3d(10px,0,0) translate3d(10px,0,0)';
Which would mean you need to append the string on every loop iteration:
div.style.transform += ' translate3d(10px,0,0)';
However, if you have too many transformations, the string would become very long and might impact the performance, let alone the readability.
If it's always the same transformation you're applying, why not increase the 10px along the way while iterating through the loop:
var offset = 10;
// then on every iteration
div.style.transform = 'translate3d(' + offset + 'px,0,0)';
offset += 10;
Taking ideas from #Arkellys answer (the accepted one), I came up with a solution which seems to be working fine for me. I am a JS newbie so I do not know how well it will hold up. Anyways here it is in case it helps anyone,
// Create container
var container = document.createElement('div');
container.style.width = '1000px';
container.style.height = '500px';
container.style.border = '2px solid black';
container.style.background = 'lightblue';
container.style.margin = '0';
container.style.padding = '10px';
document.body.appendChild(container);
var containerPosition = container.getBoundingClientRect();
// 'click' Callback function generator.
// I pass the initial position to the generator, who then generates a callback
// function which holds this value for use when the event occurs.
function clickCallbackGenerator(initialPos, increment){
var left = initialPos.left;
var clickCallback = function(event) {
var newLeftPosition = this.getBoundingClientRect().left - left + increment;
this.style.transform = `translate3d(${newLeftPosition}px, 0, 0)`;
console.log('clicked : ', this.innerHTML,' now at : ', this.getBoundingClientRect().left);
};
return clickCallback;
}
// function to create 'move-on-click' div nodes
function createNodeDiv(position, text) {
var div = document.createElement('div');
div.style.position = 'absolute';
div.style.top = `${position.top}px`;
div.style.left = `${position.left}px`;
div.style.border = '2px solid blue';
div.style.background = 'black';
div.style.color = 'white';
div.style.margin = '0';
div.style.padding = '0';
div.style.cursor = 'pointer';
div.innerHTML = text;
div.setAttribute('class', 'node');
div.setAttribute('contenteditable', 'true');
container.appendChild(div);
div.addEventListener('click', clickCallbackGenerator(div.getBoundingClientRect(), 10));
console.log('Created : ', div.innerHTML, ' at : ', div.getBoundingClientRect().left);
return div;
}
var defaultPosition = {x: containerPosition.left, y: containerPosition.top};
var increment = 10;
var nodes = [];
nodes.push(createNodeDiv({left: containerPosition.left, top: containerPosition.top}, 'hello'));
nodes.push(createNodeDiv({left: containerPosition.left+50, top: containerPosition.top+50}, 'world'));
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="description" content="Free Web tutorials">
<meta name="keywords" content="HTML,CSS,XML,JavaScript">
<meta name="author" content="John Doe">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
<script src="script.js"></script>
</body>
</html>
I used a function clickCallbackGenerator to generate the callbacks for each moveable div. That is the only thing I changed.
I have a code similar to this:
var a = document.createElement('div');
a.style.width = '200px';
a.style.fontSize = fontSize(a.offsetWidth, 5);
a.innerHTML = 'Example';
document.body.appendChild(a);
function fontSize(reference, factor){
return (reference*factor)/100 + 'px';
}
However when I run it, the element font size appears to be 0px. The console.log returns expected value tho.
Similar code works on an javascript object that I tried:
var Object = function(target){
var default = {
fontSize: fontSize(target.clientWidth, 5);
}
}
jsFiddle here
What did I do wrong?
The HTMLElement.offsetWidth read-only property returns the layout width of an element. Typically, an element's offsetWidth is a measurement which includes the element borders, the element horizontal padding, the element vertical scrollbar (if present, if rendered) and the element CSS width.
Your newly created div element has not been appended to the document when you try to read the offsetWidth, consequently it has a layout width of 0 so you end up with a font-size of 0px.
You need to put the div in the document before you measure how much space it takes up in the document.
Alternatively, you need to use a value other than offsetWidth (such as style.width.
Your logic is good, but you should append your element to the DOM before trying to retrieve its size:
var a = document.createElement('div');
a.style.width = '200px';
a.innerHTML = 'Example';
document.body.appendChild(a);
a.style.fontSize = fontSize(a.offsetWidth, 5);
function fontSize(reference, factor) {
return (reference * factor) / 100 + 'px';
}
Forked your Fiddle here
Just put a.style.fontSize = fontSize(a.offsetWidth, 5); after document.body.appendChild(a);.
var a = document.createElement('div');
a.style.width = '200px';
a.innerHTML = 'Example';
document.body.appendChild(a);
a.style.fontSize = fontSize(a.offsetWidth, 5); // <--- Move here
function fontSize(reference, factor){
return (reference*factor)/100 + 'px';
}
In fact, there's CSS properties you can't edit while the element is not added to the DOM. Font-size is one of them.
You need to append "a" to document first before you calculate its offsetWidth.
Refer - http://www.w3schools.com/jsref/prop_element_offsetwidth.asp
var a = document.createElement('div');
a.style.width = '200px';
document.body.appendChild(a);
a.style.fontSize = fontSize(a.offsetWidth, 5);
a.innerHTML = 'Example';
function fontSize(reference, factor){
return (reference*factor)/100 + 'px';
}
I'm trying to make an object to move in an area when clicking the area with the left pointer.
Is this possible in javascript/jquery?
Can you please give me some examples if possible?
I'm aiming to do this in javascript/html/css
Here's an example of an object following the mouse click
You can do it like this (Working Demo in jsFiddle):
$(document).ready(function(){
$('#canvas').click(function(e){
var offset = $(this).offset();
$('#object').css({
'top': e.pageY-offset.top,
'left': e.pageX-offset.left
})
})
})
Whenever the click event happens, it's pageX and pageY values are taken using which the exact location of the click in the canvas object is found. This coords are then used in jQuery animate function to change the location of the object div in it.
It is possible, but your answer could range from moving an object towards the click position by setting its position to absolute in CSS and modifying its left and top position until it reaches the click area with a setInterval timer to elaborate A* path finding.
var mousePosition;
var div;
div = document.createElement("div");
div.style.position = "absolute";
div.style.left = "0px";
div.style.top = "0px";
div.style.width = "100px";
div.style.height = "100px";
div.style.backgroundColor = "red";
document.body.appendChild(div);
document.addEventListener('click', function(event) {
mousePosition = {
x : event.clientX,
y : event.clientY
};
div.style.left = (mousePosition.x-div.offsetWidth/2) + 'px';
div.style.top = (mousePosition.y-div.offsetHeight/2) + 'px';
}, true);
I am writing a jQuery plugin which makes use of two nested <DIV> elements.
The outer div has a fixed width with overflow:scroll and the inner div (which is much wider) contains the content which I want to scroll around.
This all works fine apart from the fact that I want to use some JavaScript (with jQuery) to set the height of the inner div to exactly match the height of the outer div, less the height of the horizontal scroll bar.
At the moment I'm setting it to the height of the outer div, less about 20 pixels. This sort of works, but it's not going to be browser independent and is definately a hack!
Any help would be very much appreciated!
You need to use element.clientHeight. In jQuery that would be something like:
var heightWithoutScrollbar = $('#outer')[0].clientHeight;
I found a function which can get the width of a scrollbar
function getScrollBarWidth () {
var inner = document.createElement('p');
inner.style.width = "100%";
inner.style.height = "200px";
var outer = document.createElement('div');
outer.style.position = "absolute";
outer.style.top = "0px";
outer.style.left = "0px";
outer.style.visibility = "hidden";
outer.style.width = "200px";
outer.style.height = "150px";
outer.style.overflow = "hidden";
outer.appendChild (inner);
document.body.appendChild (outer);
var w1 = inner.offsetWidth;
outer.style.overflow = 'scroll';
var w2 = inner.offsetWidth;
if (w1 == w2) w2 = outer.clientWidth;
document.body.removeChild (outer);
return (w1 - w2);
};
OS Scrollbars are uniform in width, whether displayed vertically or horizontally, so you can use the width returned as the height of a horizontal scrollbar.
Edit: My original code same didn't work, however I've updated my code to a working function. You can see it in action here: http://jsbin.com/ejile3
The page will alert the width of the scrollbar.