Why transform-origin property is not working? - javascript

I have a mini-question concerning the return value of transform-origin in javascript :
I have a simple div with a transform-origin set to 0 0 via CSS and i'm trying to console.log() the div.style.transformOrigin but I get nothing, not even an error... What am I doing wrong ?
In here link it says "Return the transformOrigin property: object.style.transformOrigin".
That's what I'm trying to do...
let div_1 = document.querySelector('#div1');
console.log('the transform origin is : ', div_1.style.transformOrigin);
#div1 {
width: 100px;
height: 300px;
transform-origin: 0 0;
background-color: red;
}
<div id="div1">hello</div>

In the reliable source we can read that:
The style read-only property returns the inline style of an element in the form of a CSSStyleDeclaration object that contains a list of all styles properties for that element with values assigned for the attributes that are defined in the element's inline style attribute.
Try using:
getComputedStyle(div_1).getPropertyValue('transform-origin')
element.style works only if you modify it directly, not on the class or id
let div_1 = document.querySelector('#div1');
console.log('the transform origin is : ', getComputedStyle(div_1).getPropertyValue('transform-origin'));
#div1 {
width: 100px;
height: 300px;
transform-origin: 0 0;
background-color: red;
}
<div id="div1">hello</div>
Sidenote: w3schools isn't a reliable source of information
let div_1 = document.querySelector('#div1');
div_1.style.transformOrigin = '0 0'
console.log('the transform origin is : ', div_1.style.transformOrigin);
#div1 {
width: 100px;
height: 300px;
background-color: red;
}
<div id="div1">hello</div>

Related

How to use javascript to dynamically set css animation's keyframe?

I want to make an animation on my product page. When user clicks "add to cart" the product image will be animated moving and shrinking to the cart icon in the nav bar.
Here is a sample html
$('div.test').on('animationend', (e) => {
$(e.target).remove();
})
//user click
$('div.test').addClass('animateTest');
.test {
position : fixed;
top : 200px;
left : 600px;
background : red;
width : 200px;
height : 300px;
}
#keyframes toCart {
25% {
top : 850px;
left : 550px;
width : 200px;
height : 300px;
}
100% {
top : 100px;
left : 1100px;
width : 0;
height : 0
}
}
.animateTest {
animation : toCart 2s;
/* animation-fill-mode: forwards; */
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="test">
</div>
The hard part is, since users' viewports vary, I probably need to use javascript to get the cart icon's position(unless I can get it from CSS which I don't think is possible):
whereIsCart = $('#cartIcon').offset()
and I need to do something like
100% {
top : whereIsCart.top;
left : whereIsCart.left;
width : 0;
height : 0
}
But how can I do this?
Or, is there any better practice to achieve the same goal?
It may be easier to use css transitions instead of keyframe animations:
.test {
// ...
transition: transform 1s ease-in-out;
}
// on click
whereIsCart = $('#cartIcon').offset();
$('div.test').css('transform', 'translate(' + whereIsCart.left + 'px, ' + whereIsCart.top + 'px) scale(0)');
When working with CSS in JavaScript you may want to use the CSSOM API; more specifically, its factory functions for unit values, e.g. CSS.px(), CSS.percent().
Note that parts of the CSSOM API are still experimental, e.g. the factory functions. In production, you should make sure that the target browsers support the features you use.
Regardless of using CSS or JS for the animation itself: To get the element's current position in the viewport you can use Element.getBoundingClientRect(), or more generally Element.getClientRects() for all its relevant boxes.
CSS Custom Properties
You can use custom properties for the initial position. You can set them via JavaScript, and even provide a fallback value in CSS.
If you use them for the animating (not as animated) properties, it should just work:
const divTest = document.querySelector("div.test");
// Example for non-empty custom properties
divTest.style.setProperty("--top", CSS.px(20));
divTest.style.setProperty("--left", CSS.px(80));
// Should happen on click:
toCart(divTest);
function toCart(element) {
const rect = element.getBoundingClientRect();
element.style.setProperty("--top", CSS.px(rect.top));
element.style.setProperty("--left", CSS.px(rect.left));
element.classList.add("animateTest");
}
.test {
position: fixed;
top: var(--top, 10%);
left: var(--left, 10%);
width: 200px;
height: 300px;
background: red;
}
#keyframes toCart {
25% {
top: 80%;
left: 50%;
width: 200px;
height: 300px;
}
100% {
top: 10%;
left: 100%;
width: 0;
height: 0;
}
}
.animateTest {
animation: toCart 2s;
}
<div class="test"></div>
Sidenote: If you want to animate custom properties themselves, you have to define the in a #property rule. Otherwise CSS cannot animate it since its type may be anything (animating e.g. from a length to a color is impossible).
Web Animations API
In JavaScript, you can use the Web Animations API, which is essentially CSS animations but in JS.
You can define keyframes, duration, fill-mode and more. Since Animation.finished is a promise, you can simply react to the animation's end via await or Promise.then().
Example:
const divTest = document.querySelector("div.test");
// Should happen on click:
const animation = animateToCart(divTest);
animation.finished.then(() => console.log("Animation finished. This could start a new animation!"));
function animateToCart(element) {
const rect = element.getBoundingClientRect();
const keyframes = [
{
offset: .25,
top: CSS.percent(80),
left: CSS.percent(50),
width: CSS.px(rect.width),
height: CSS.px(rect.height)
}, {
top: CSS.percent(10),
left: CSS.percent(100),
width: 0,
height: 0
}
];
return element.animate(keyframes,
{
duration: 2000,
easing: "ease" // Is default in CSS, but not in Web Animations...
}
);
}
.test {
position: fixed;
top: 10%;
left: 10%;
width: 200px;
height: 300px;
background: red;
}
<div class="test"></div>
Multi-step animations are also easily done with Web Animations, since you can start another animation after the first animation's promise has resolved.
CSS variables sample code...
const
bluElm = document.querySelector('#blue_elm')
, btAnim = document.querySelector('#bt-anim')
, btMovE = document.querySelector('#bt-movE')
, elTest = document.querySelector('.test')
;
btMovE.onclick = () =>
{
bluElm.classList.toggle('move');
}
btAnim.onclick = () =>
{
let rect = bluElm.getBoundingClientRect();
/* change CSS variables values as style Property ------------- */
elTest.style.setProperty('--p_top', `${rect.bottom}px`);
elTest.style.setProperty('--p_left', `${rect.left}px`);
elTest.classList.add('animateTest');
}
elTest.onanimationend = () =>
{
elTest.classList.remove('animateTest');
}
#blue_elm {
position : fixed;
top : 20px;
left : 300px;
width : 20px;
height : 20px;
border-radius : 10px;
background : cornflowerblue;
}
#blue_elm.move {
top : 50px;
left : 150px;
}
.test {
position : fixed;
top : 200px;
left : 600px;
background : red;
width : 200px;
height : 300px;
--p_top : 0; /* CSS variables declaration */
--p_left : 0;
}
.animateTest {
animation : toCart 2s;
}
#keyframes toCart {
25% {
top : 850px;
left : 550px;
width : 200px;
height : 300px;
}
100% {
top : var(--p_top); /* CSS variables usage */
left : var(--p_left);
width : 0;
height : 0
}
}
<button id="bt-anim"> show animation</button>
<button id="bt-movE"> move element +- 150px</button>
<div id="blue_elm"></div>
<div class="test"></div>

getComputedStyle() consistency across browsers

I need to get a computed CSS position for an element, but when using auto instead of a number value I get inconsistent results across browsers.
For example, in the demo below when setting bottom: auto;
Chrome and Firefox reports auto, but Edge reports 0px
var el1 = document.querySelector('#one');
el1.innerText = getComputedStyle(el1).bottom;
var el2 = document.querySelector('#two');
el2.innerText = getComputedStyle(el2).bottom;
#one, #two {
position: sticky;
padding: 20px;
color: white;
}
#one {
bottom: 0px;
background: red;
}
#two {
bottom: auto;
background: blue;
}
<div id="one"></div>
<div id="two"></div>
Is there some other way to get the actual computed value of auto consistently across browsers?
In the Edge inspector you can see (screenshot below) the actual set value is auto but it shows the computed value as 0px
Screenshots
Chrome 74.0.3729.131
Edge 44.17763.1.0 | EdgeHTML 18.17763
There have been recent changes in this area.
Previously, getComputedStyle was returning the Computed Values of an element, now it should return its Resolved Values.
For the bottom property the rules to get this resolved value are:
A resolved value special case property like top
defined in another specification
If the property applies to a positioned element and the resolved value of the display property is not 'none' or 'contents', and the property is not over-constrained, then the resolved value is the used value. Otherwise the resolved value is the computed value.
It sounds like your browser treats your elements as a positioned element and thus uses the used value (0px) instead of the computed value (the keyword 'auto' or a computed <length-percentage> value).
I must admit I'm not quite clear as to why all the other browsers don't consider your sticky elements as positioned elements, I would have thought they were also, but they do agree that a relatively positioned element returns the resolve value '0px',
var el1 = document.querySelector('#one');
el1.innerText = getComputedStyle(el1).bottom;
var el2 = document.querySelector('#two');
el2.innerText = getComputedStyle(el2).bottom; // '0px' everywhere
#one, #two {
position: relative;
padding: 20px;
color: white;
}
#one {
bottom: 0px;
background: red;
}
#two {
bottom: auto;
background: blue;
}
<div id="one"></div>
<div id="two"></div>
While a non-positioned one returns the computed-value 'auto'.
var el1 = document.querySelector('#one');
el1.innerText = getComputedStyle(el1).bottom;
var el2 = document.querySelector('#two');
el2.innerText = getComputedStyle(el2).bottom; // 'auto' everywhere
#one, #two {
position: static;
padding: 20px;
color: white;
}
#one {
bottom: 0px;
background: red;
}
#two {
bottom: auto;
background: blue;
}
<div id="one"></div>
<div id="two"></div>
Unfortunately, I don't think there is a way to get consistent values across browsers that did implement these changes, the ones that didn't, and Edge, unless if you can avoid positioning your elements, then you should get 'auto' everywhere.
I've tried your code and reprodcued your issue on my side.
You could check from MDN and Can I Use that getComputedStyle() is normally supported by most modern browsers.
From my point of view, the different result of bottom value may be the browsers' self behaviour to show the bottom attribute.
Chrome read the bottom as auto but Edge read it as 0.
I've also test that if I set the width as auto, the result will be the same.
#two {
bottom: auto;
background: blue;
width:auto;
}
el2.innerText = getComputedStyle(el2).width;

why is child element's width changing when parent's width changes?

I am trying to create a tooltip element that has a min width of 50px and a max width of 200px. I place the tooltip element inside another element so that I can easily control when the tooltip appears or disappears when there is a hover event on the parent.
The problem that I have is that the tooltip element's width appears to be controlled by the parent's width even though I specified that the child(tooltip) has an absolute position.
let p = document.getElementById( 'parent' );
let b = true;
setInterval( ()=> {
b = !b;
let w = 10;
if( b ) {
w = 300;
}
p.style.width = `${w}px`
}, 5000 );
#parent {
background-color: cyan;
width: 100px;
height: 25px;
position: relative;
transition: width 2s;
}
#tooltip {
position: absolute;
top: calc( 100% + 5px );
left: 5px;
min-width: 50px;
max-width: 200px;
background-color: yellow;
padding: 5px;
border: 1px solid black;
}
<div id="parent">
<div id="tooltip">
My long tooltip text that wraps to multiple lines as needed.
</div>
</div>
I would like the tooltip (yellow div) to keep it's size at 200px in this example, but we can see that when the parent changes width, the tooltip width also changes. Why?
Is there a way to fix this problem?
Clarification: In this example: https://codepen.io/anon/pen/ePPWER we see that the tooltip text looks nice on one line. I don't want the tooltip's div to change its width when the parent changes width, because it forces the tooltip text to wrap onto 2 lines which is undesirable.
If we check the specification related to the width of absolutely positioned element we can read this:
'width' and 'right' are 'auto' and 'left' is not 'auto', then the width is shrink-to-fit . Then solve for 'right'
So in your case the width of your element is shrink to fit:
Calculation of the shrink-to-fit width is similar to calculating the
width of a table cell using the automatic table layout algorithm.
Roughly: calculate the preferred width by formatting the content
without breaking lines other than where explicit line breaks occur,
and also calculate the preferred minimum width, e.g., by trying all
possible line breaks. CSS 2.1 does not define the exact algorithm.
Thirdly, calculate the available width: this is found by solving for
'width' after setting 'left' (in case 1) or 'right' (in case 3) to 0.
Then the shrink-to-fit width is: min(max(preferred minimum width,
available width), preferred width).
To make it easy, and without considering the min/max-width, the width of your element will try to fit the content without exceding the width of its parent container (containing block). By adding min/max-width you simply add more constraint.
One idea of fix it to remove positon:relative from the parent element so that it's no more the containing block of the position:absolute element (it will be the initial containing block which is wide enough to avoid the available width constraint).
Then use margin instead of top/left to control the position:
let p = document.getElementById( 'parent' );
let b = true;
setInterval( ()=> {
b = !b;
let w = 10;
if( b ) {
w = 300;
}
p.style.width = `${w}px`
}, 5000 );
#parent {
background-color: cyan;
width: 100px;
height: 25px;
transition: width 2s;
}
#tooltip {
position: absolute;
margin-top: 30px;
min-width: 50px;
max-width: 200px;
background-color: yellow;
padding: 5px;
border: 1px solid black;
}
<div id="parent">
<div id="tooltip">
My long tooltip text that wraps to multiple lines as needed.
</div>
</div>
ID Tooltip is being used under Parent. When parent's width changes, it also suggest that tooltip's total width is changed. Since you have used mix-width and max-width it will expand till it reaches max-width. If you want it to be fixed then simple use width.
It is because the .parent has a position: relative. This will keep all children (position: absolute included) as confined by the parent div.
Not sure if this will work for you because it is pulling the tooltip out of the parent and making it's own with span wrapping the text. Alternatively, you'll need to change the parent from being relative otherwise it'll continually affect the child.
let p = document.getElementById('parent');
let b = true;
setInterval(() => {
b = !b;
let w = 10;
if (b) {
w = 300;
}
p.style.width = `${w}px`
}, 5000);
#parent {
background-color: cyan;
width: 100px;
height: 25px;
transition: width 2s;
position: relative;
}
#root {
position: relative;
}
#tooltip {
width: 100%;
}
#tooltip span {
position: absolute;
top: calc( 100% + 5px);
left: 5px;
min-width: 50px;
max-width: 200px;
background-color: yellow;
padding: 5px;
border: 1px solid black;
}
<div id="root">
<div id="parent"></div>
<div id="tooltip">
<span>My long tooltip text that wraps to multiple lines as needed.</span>
</div>
</div>

Why does .getComputedStyle() appear to be the only commonly used method to return the incorrect height of an element?

Question: Under certain circumstances .getComputedStyle() appears to return an incomplete/incorrect value for the height property of an element. Now I'm worried. Is it known to return unpredictable or incorrect values for other properties?
Backstory: I had been using .getComputedStyle() until I noticed that it was returning what appeared to be incorrect values for the heights of elements. So I tested it against several other methods.
In the simple test code below all of these returned 400 which is the correct height of the element that was tested.
.offsetHeight
.scrollHeight
.clientHeight
.getBoundingClientRect().height
However .getComputedStyle().height returned 300px which is the height of the element before its padding is applied.
'use strict';
window.addEventListener('load', measureDiv)
function measureDiv() {
console.log('offsetHeight = ' +document.querySelector('.container').offsetHeight);
console.log('scrollHeight = ' +document.querySelector('.container').scrollHeight);
console.log('clientHeight = ' +document.querySelector('.container').clientHeight);
console.log('getBoundingClientRect().height = ' +document.querySelector('.container').getBoundingClientRect().height);
console.log('getComputedStyle().height = ' +window.getComputedStyle(document.querySelector('.container')).height);
}
body {
margin: 0;
padding: 0;
}
.container {
margin: 120px;
padding: 50px;
background-color: green;
}
.box_1 {
padding: 20px;
background-color: blue;
}
.image_container {}
<div class="container">
<div class="box_1">
<div class="image_container"><img src="https://www.cis.rit.edu/~cnspci/courses/common/images/checkerboard-256x256.jpg"></div>
</div>
</div>

Cannot set div top attribute

I'm trying to change the top value of a div from Javascript, but for some reason it's not working and just sticks with the CSS value. So in this test I try and change it to 500, but it stays at 50. Whatever value I change in CSS will reflect in the position no matter what value I try and over-ride it with in Javascript. If I remove the top from CSS, the value will be 0 no matter what.
Am I missing something obvious here?
var tQuestItemContainer = document.createElement('div');
tQuestItemContainer.id = 'popup_quests_item_container';
tQuestItemContainer.top = 500 + "px";
alert('top: ' + tQuestItemContainer.top);
tQuestContainer.appendChild(tQuestItemContainer);
Here's the CSS
#popup_quests_item_container{
position: absolute;
width: 320px;
height: 30px;
top:50px;
left:13px;
background-color: #000000;
border: 1px solid #676669;
cursor: pointer;
}
You need to set the style attribute:
tQuestItemContainer.style.top = 500 + "px";

Categories

Resources