getBoundingClientRect not working as expected on body after transform - javascript

$( document ).ready(function() {
$('body').append('<div id="tester2"></div>');
$('#tester2').css({
position:'absolute',
background:'blue',
width: 10,
height:10
});
setInterval(function(){
var x = $('#tester')[0].getBoundingClientRect();
$('#tester-pos').text('top: ' + x.top + ', left:' + x.left);
$('#tester2').css({
top:x.top,
left:x.left
});
}, 1000);
$('#jquery-version').text('jquery version: ' + $.fn.jquery);
});
#tester{
position:absolute;
top:50px;
left:50px;
width:10px;
height:10px;
background:red;
}
#page{
min-height:200px;
}
body{
border:2px solid green;
transform: scale(1) translate(20px, 40px);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="tester">
</div>
<div id="page">
getBoundingClientRect on red tester returned:
<span id="tester-pos"></span>
<div id="jquery-version"></div>
</div>
I need to place a div over an existing div. The existing div is contained inside the body of and HTML document that has the CSS transform property set. I need to place the new div after the document was rendered and transformed.
When I call getBoundingClientRect() on the div that I need to hide (red square in attached fiddle) I get the wrong top/left. I set the top/left of the blue square to the output of getBoundingClientRect() and they do not overlap.
setInterval(function(){
var x = $('#tester')[0].getBoundingClientRect();
$('#tester-pos').text('top: ' + x.top + ', left:' + x.left);
$('#tester2').css({
top:x.top,
left:x.left
});
}, 1000);
How can this be solved?

You can just take the offsetTop and offsetLeft without worrying about the transform as the same transform is applied to the newly added div as well. I'm guessing that from the example.
$(document).ready(function() {
$('body').append('<div id="tester2"></div>');
$('#tester2').css({
position: 'absolute',
background: 'blue',
width: 10,
height: 10,
opacity: 0.6
});
var tester = document.getElementById('tester');
$('#tester2').css({
top: tester.offsetTop - 2, // 2px border for body
left: tester.offsetLeft - 2
});
$('#jquery-version').text('jquery version: ' + $.fn.jquery);
});
#tester {
position: absolute;
top: 50px;
left: 50px;
width: 10px;
height: 10px;
background: red;
}
#page {
min-height: 200px;
}
body {
border: 2px solid green;
transform: scale(1) translate(20px, 40px);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js"></script>
<div id="tester">
</div>
<div id="page">
getBoundingClientRect on red tester returned:
<span id="tester-pos"></span>
<div id="jquery-version"></div>
</div>

I have an answer that works, it may not be the best answer yet, but it works 100% for me.
$( document ).ready(function() {
$('body').append('<div id="tester2"></div>');
$('#tester2').css({
position:'absolute',
background:'blue',
width: 10,
height:10,
});
setInterval(function(){
var x = $('#tester')[0].getBoundingClientRect();
$('#tester-pos').text('top: ' + x.top + ', left:' + x.left);
$('#tester2').css({
'top':x.top/2,
'left':(x.left/2)+x.width
});
}, 1000);
$('#jquery-version').text('jquery version: ' + $.fn.jquery);
});
#tester{
position:absolute;
top:50px;
left:50px;
width:10px;
height:10px;
background:red;
}
#page{
min-height:200px;
}
body{
border:2px solid green;
transform: scale(1) translate(20px, 40px);
}
#tester2{
transform: scale(1) translate(0, 0);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="tester">
</div>
<div id="page">
getBoundingClientRect on red tester returned:
<span id="tester-pos"></span>
<div id="jquery-version"></div>
</div>
Edit
The only time that my potential solution gets a bit weird is when you have to scroll... So be warned there.

Related

More than 1 modal?

I have this modal, and would like to make many of them on the same page. I tried a lot, but nope. Any idea how to do this? Thanks.
Here the code:
HTML
<div class="containerofelements">
<div id="s2-01" class="resizeratio">1.00</div>
<div id="s-wrap" class="wrap" style="transform: scale(0.04);">
<div class="elements" style="transform: scale(0.04);">
<div id="s-rr"class="resizeratio">0.04</div>
<div id="s-001" class="slider16" midicc="1, 80" colour="#ff3300"></div>
</div>
</div>
</div>
CSS
.containerofelements {
}
.resizeratio {
display: inline-block;
cursor: pointer;
border-style: solid;
color: red;
width: 50px;
height: 20px;
}
.wrap {
background-color: rgba(0, 80, 128, 1);
transform-origin: 1880px 0;
}
.elements {
width: 1600px;
height: 800px;
transform-origin: 1880px 0;
}
JS
$(document).ready(function() {
$(".resizeratio").click(function() {
var el = $('.elements');
var scale = $(this).text();
el.css({
transform: "scale(" + scale + ")"
});
});
});
$(document).ready(function() {
$(".resizeratio").click(function() {
var el = $('.wrap');
var scale = $(this).text();
el.css({
transform: "scale(" + scale + ")"
});
});
});
I tried to apply separate names, IDs, but no good results without replicating everything which is not a good practice I suppose, a waste of code.

How to retrieve the real size of a div after transform scale is applied?

After I applied a transform: scale(n) to a div, the width and height I obtain via javascript stay the same.
The following illustrates the setup.
console.log($('.parent1').width())
console.log('parent1 w ' + $('.parent1 .container').width())
console.log('parent1 h ' + $('.parent1 .container').height())
$('.parent2').css('transform', 'scale(1.5)')
console.log('parent2 w ' + $('.parent2 .container').width())
console.log('parent2 h ' + $('.parent2 .container').height())
.parent {
width: 250px;
}
.parent1 {
height: 100px;
}
.parent2 {
height: 100px;
}
.container {
display: block;
height:50%;
width:50%;
margin:auto;
border:solid 1px red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="parent parent1">
<div class="container">
</div>
</div>
<div class="parent parent2">
<div class="container">
</div>
</div>
As you can see from the print out in the console, the width and height of the second container is the same as the first one. However for a user the second container is actually larger.
How can I obtain the actual size of $('.parent2 .container')?
The scale is applied from another process and the scale factor is not accessible to me.
Use getBoundingClientRect()
console.log($('.parent1').width())
console.log('parent1 w ' + $('.parent1 .container').width())
console.log('parent1 h ' + $('.parent1 .container').height())
$('.parent2').css('transform', 'scale(1.5)')
console.log('parent2 w ' + $(".parent2 .container")[0].getBoundingClientRect().width)
console.log('parent2 w ' + $(".parent2 .container")[0].getBoundingClientRect().height)
.parent {
width: 250px;
}
.parent1 {
height: 100px;
}
.parent2 {
height: 100px;
}
.container {
display: block;
height:50%;
width:50%;
margin:auto;
border:solid 1px red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="parent parent1">
<div class="container">
</div>
</div>
<div class="parent parent2">
<div class="container">
</div>
</div>

Javascript - Creating Faded Stacked Bar Chart

I encounter the following problem: how can I create a stacked bar chart that can hold 3 variables only. (x, y, z) and x + y + z = 100%.
What's important is how can I make the colors edges between x y and y z of the bars faded as shown in the figure below? (Any popular library can be used)
You can use a single linear gradient in css with four percentage locations to achieve the desired result. You don't need to specify the 0% and 100% colors, but you do need to start and end the fades a few percentage points on either side or you'll get a hard color change. Here's a function that will help you center the labels too, but it doesn't handle validation or edge cases.
function updateGradientBar(agree, depends, disagree) {
let
padding = 3,
agreeFadeStart = agree - padding,
dependsFadeStart = agree + padding,
dependsFadeEnd = agree + depends - padding,
disagreeFadeStart = agree + depends + padding,
labelAgree = $('#gradient-bar .label.agree'),
agreeLabelPosition = agree / 2,
labelDepends = $('#gradient-bar .label.depends'),
dependsLabelPosition = agree + (depends / 2),
labelDisagree = $('#gradient-bar .label.disagree'),
disgreeLabelPosition = agree + depends + (disagree / 2);
$('#gradient-bar').css(
'background',
'linear-gradient(to right, green ' + agreeFadeStart + '%,' + 'orange ' + dependsFadeStart + '%, orange ' + dependsFadeEnd + '%,' + 'red ' + disagreeFadeStart + '%)');
labelAgree.css('left', agreeLabelPosition + '%').text(agree + '%');
labelDepends.css('left', dependsLabelPosition + '%').text(depends + '%');
labelDisagree.css('left', disgreeLabelPosition + '%').text(disagree + '%');
}
updateGradientBar(35, 40, 25);
#gradient-bar {
height: 20px;
border-radius: 4px;
background: blue;
position: relative;
}
#gradient-bar .label {
color: white;
font-weight: bold;
position: absolute;
top: 50%;
transform: translatex(-50%) translateY(-50%);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="gradient-bar">
<div class="label agree"></div>
<div class="label depends"></div>
<div class="label disagree"></div>
</div>
I made a static solution for that using CSS:
You only have to "replace" the % unities with the appropriate values using javascript (this will not be a big deal).
The Solution: first: there is no possiblity to intersect colors in pure css. so I added to absolute positioned divs on the bar with a background color containing transparency.
HTML:
<div class="bar">
<div class="agree">
20%
</div>
<div class="seperator part1">
</div>
<div class="independants">
30%
</div>
<div class="seperator part2">
</div>
<div class="disagree">
50%
</div>
</div>
And the CSS:
.bar {
display: flex;
width: 100%;
border: 1px solid #000;
border-radius: 3px;
}
.bar > div {
height: 20px;
color: #fff;
text-align: center;
}
.agree {
flex: 1 0 20%;
background: green;
}
.independants {
flex: 1 0 30%;
background: orange;
}
.disagree {
flex: 1 0 50%;
background: red;
}
.seperator {
position: absolute;
width: 5%;
z-index: 20;
}
.part1 {
left: 16%;
background: linear-gradient(to right, rgba(122,188,255,0) 0%,rgba(249,186,97,0.44) 44%,rgba(237,176,64,1) 100%);
}
.part2 {
left: 50%;
background: linear-gradient(to right, rgba(255,164,28,1) 0%,rgba(249,186,97,0.56) 44%,rgba(237,176,64,0) 100%);
}
Check out the Fiddle for it here: https://jsfiddle.net/taxostd0/5/

This code won't work// How to get the div to slide in-line with other divs on mouse over?

I am aware there are discussions similar and I have read them, analysed them and tried them in my code. The code below is what I currently have, trust me, I have spent all day exhausting all methods to prevent me from asking here but I give up! This is by far the most frustrating goal.
I would like to have a header with a little bar that slides across above each menu item on hover over. A perfect example of what I would like is located here at http://www.wix.com/ . Please visit and move your mouse over the navigation bar and you'll understand instatly what I am trying to achieve.
Here is my current code...
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Untitled Document</title>
<style>
div {
display: inline-block;
float:left;
}
#red {
background-color:#FF0000;
height:100px;
width:100px;
}
#blue {
background-color:#0000FF;
height:100px;
width:200px;
}
#yellow {
background-color:#E2BE22;
height:100px;
width:50px;
}
#green {
background-color:#008800;
height:100px;
width:170px;
}
#slider{
background-color:#6FF;
height:10px;
width:100px;
position:relative;
}
</style>
</head>
<body>
<div id="slider"></div><br />
<div id="red"></div>
<div id="blue" onmouseover="javascript:movetoblue()" onmouseout="javascript:exitblue()"></div>
<div id="yellow" onmouseover="javascript:movetoyellow()" onmouseout="javascript:exityellow()"></div>
<div id="green" onmouseover="javascript:movetogreen()" onmouseout="javascript:exitgreen()"></div>
</body>
</html>
<script>
var slider = document.getElementById( 'slider' );
function movetoblue(){
var slider = $("#slider");
slider.animate({left: '100px', width: '160px'}, "slow");
}
function exitblue(){
var slider = $("#slider");
slider.animate({left: '7px', width: '200px'}, "slow");
}
function movetoyellow(){
var slider = $("#slider");
slider.animate({left: '100px', width: '160px'}, "slow");
}
function exityellow(){
var slider = $("#slider");
slider.animate({left: '7px', width: '200px'}, "slow");
}
function movetogreen(){
var slider = $("#slider");
slider.animate({left: '100px', width: '160px'}, "slow");
}
function exitgreen(){
var slider = $("#slider");
slider.animate({left: '7px', width: '200px'}, "slow");
}
</script>
I know much is probably wrong with it. Sigh. But any help would be much appreciated. Thank you :)
PS: I would like this to work on Chrome, IE, Safari and Firefox, but I'm mainly concerned about Chrome, IE, Safari. Thanks again!
As I see you are using jQuery but in a very wrong way. I've created a JSFiddle for you. take a look at this
Update 1:
Edited The Code For Better Performance By Adding:
$("#slider").stop()
$(document).ready(function() {
$("#slider").animate({
"left": $(".item:first").position().left + "px",
"width": $(".item:first").width() + "px"
}, 0);
$(".item").hover(function() {
$("#slider").stop()
$("#slider").animate({
"left": $(this).position().left + "px",
"width": $(this).width() + "px"
}, 500);
});
});
div {
display: inline-block;
float: left;
}
#red {
background-color: #FF0000;
height: 100px;
width: 100px;
}
#blue {
background-color: #0000FF;
height: 100px;
width: 200px;
}
#yellow {
background-color: #E2BE22;
height: 100px;
width: 50px;
}
#green {
background-color: #008800;
height: 100px;
width: 170px;
}
#slider {
background-color: #6FF;
height: 10px;
width: 100px;
position: absolute;
left: 0;
top: 0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="slider"></div>
<div id="red" class="item"></div>
<div id="blue" class="item"></div>
<div id="yellow" class="item"></div>
<div id="green" class="item"></div>
Update 2:
For Deining The Start Position You Should Replace This Part:
$("#slider").animate({
"left": $(".item:first").position().left + "px",
"width": $(".item:first").width() + "px"
}, 0);
With This:
$("#slider").animate({
"left": $("#TAG_ID").position().left + "px",
"width": $("#TAG_ID").width() + "px"
}, 0);
NOTE TAG_ID is your starting div id property
Update 3:
In case that user didn't select a tab:
$("#slider").delay(3000).animate({
"left": $(this).position().left + "px",
"width": $(this).width() + "px"
}, 500);

How to get original position of an element (DIV) after it's been rotated?

For a DIV which has been rotated using CSS transform.
I need get original position of an DIV (top, left, bottom, right, width, height) before rotation was applied (look at gray div for an example).
http://jsfiddle.net/akycc4t1/7/
I know that I can remove CSS rotation and use Element.getBoundingClientRect();
to get the value, but I was wondering if there is a better approach using math/geometry.
My needs is to re-create programmatically a DIV positioned exactly on top of the original one using coordinates relative to the top-left of the viewport.
Other methods or idea area also welcome.
<div id="example" style="display: none;"></div>
<div id="target" class="rotation"></div>
<div id="trace"></div>
<button id="btn-rotate" type="button">rotate</button>
<button id="btn-coordinate" type="button">get coordinate x,y</button>
<button id="btn-coordinate-notrotated" type="button">get coordinate as target was not rotated</button>
document.getElementById('btn-rotate').addEventListener('click', function (event) {
document.getElementById('target').classList.add('rotation');
document.getElementById('example').style.display = '';
}.bind(this));
document.getElementById('btn-coordinate').addEventListener('click', function (event) {
alert(JSON.stringify(document.getElementById('target').getBoundingClientRect()));
}.bind(this));
document.getElementById('btn-coordinate').addEventListener('click', function (event) {
alert(JSON.stringify(document.getElementById('target').getBoundingClientRect()));
}.bind(this));
document.getElementById('btn-coordinate-notrotated').addEventListener('click', function (event) {
// solution here
alert('{top: xxxx,left: xxxx, right: xxxpx, width: 200, height: 200}');
}.bind(this));
#target {
position:absolute;
top:100px;
left:100px;
width:200px;
height:200px;
background-color: cyan;
transform-origin: 50% 50% 0;
}
#trace {
position:absolute;
top:100px;
left:100px;
width:200px;
height:200px;
border: 1px solid darkgray;
opacity: 0.1;
}
#example {
position:absolute;
top:100px;
left:100px;
width:5px;
height:5px;
background-color: red;
z-index: 100;
}
.rotation {
-ms-transform: rotate(10deg);
-webkit-transform: rotate(10deg);
transform: rotate(10deg);
}
Utilizing .css(), try
document.getElementById('btn-coordinate-notrotated')
.addEventListener('click', function (event) {
// solution here
var styles = $("#target")
.css(["height", "width", "top"
, "bottom", "left", "right"
, "background", "position"]);
$("<div />", {"class":"clone", "css":styles}).appendTo("body");
alert('{top: ' + styles.top + ',left: '
+ styles.left + ', right: '
+ styles.right + ', width: '
+ styles.width + ', height: '
+ styles.height + '}');
}.bind(this));
jsfiddle http://jsfiddle.net/akycc4t1/9/
// solution here
var style = getComputedStyle(document.getElementById('target'));
alert('{top: ' + style.top + ', left: ' + style.left + ', width: 200, height: 200}');

Categories

Resources