javascript: change CSS position by relative amount - javascript

Basically what I'm trying to do is every time the JavaScript is executed (by click), the CSS left value for my <div> descreases by 20%. So, for example, left starts at 40%. The first time you click it should become 20%, and then 0%, and then -20%, and so on. I've updated my code below:
FilmstripLeft = parseInt(document.defaultView.getComputedStyle(ul).left, 10) / 4;
ul.style.left = (FilmstripLeft - 20) + "%";
ul is a <div> I get using document.getElementById(). This code works now, but I'm unsure about the divided by 4 at the end of the first line. I tested than that line was returning a value of 160 before I added the / 4 at the end, even though the starting value is 40%. I'm not sure if this is because it's returning the value in pixels instead of percent, in which case dividing by 4 would only work if left is at 160px?

"left" value for my div descreases by 20%
No. According to your explanation, you want to decrease it by 20 percentage points, not by 20%.
getComputedStyle(ul).left
Be aware percentages are relative units. When computed, they are transformed to an absolute length.
You can only access the raw percentage as an inline style
var target = document.getElementById("target");
document.querySelector('input').addEventListener('click', function() {
target.style.left = parseFloat(target.style.left) - 20 + '%';
});
#target {
position: relative;
width: 30px;
height: 30px;
background: blue;
}
<div id="target" style="left: 80%"></div>
<input type="button" value="Click" />
But you don't need the raw percentage in order to subtract 20 percentage points. You can use calc:
var target = document.getElementById("target");
document.querySelector('input').addEventListener('click', function() {
var current = getComputedStyle(target).left;
target.style.left = 'calc(' + current + ' - 20%)';
});
#target {
position: relative;
left: 80%;
width: 30px;
height: 30px;
background: blue;
}
<div id="target"></div>
<input type="button" value="Click" />

getComputedStyle() won't work well for your purposes because it, by definition, returns the computed value of the style in terms of pixels, not the relative percentage of the original value. In order to calculate what "20%" is using computed styles, you will have to do it manually by finding the parent element's width and then taking 20% of that:
function onClick(){
var myElement = document.getElementById("inner");
var leftValue = parseFloat(window.getComputedStyle(myElement).left);
var parentElement = myElement.parentElement;
var parentWidth = parseFloat(window.getComputedStyle(parentElement).width);
var twentyPercent = parentWidth * 0.20;
var newLeftValue = (leftValue - twentyPercent) + "px";
myElement.style.left = newLeftValue;
updateStatus();
}
function updateStatus(){
document.getElementById("status").innerHTML = window.getComputedStyle(document.getElementById("inner")).left;
}
updateStatus();
document.getElementById("btn").addEventListener("click", onClick);
#outer {
width: 400px;
height: 20px;
background: #888;
}
#inner {
width: 20px;
height: 20px;
position: relative;
left: 80%;
background: red;
}
<div id="outer">
<div id="inner"></div>
</div>
<button id="btn">click me</button>
<div id="status"></div>
The downside of this method is that it fixes left as a pixel value instead of a true percentage of the parent, so if the window/parent width changes, the left value won't match properly.
It's easier if the left value is an inline HTML style instead of a CSS style because you can get it directly with element.style, which reads the inline HTML style value. Then you can assign a new left value as a true percentage that will update if the window/parent width changes.
function onClick(){
var myElement = document.getElementById("inner");
// Note how #inner's left value is now inline instead of CSS
var leftValue = parseFloat(myElement.style.left);
var newLeftValue = (leftValue - 20) + "%";
myElement.style.left = newLeftValue;
updateStatus();
}
function updateStatus(){
document.getElementById("status").innerHTML = document.getElementById("inner").style.left;
}
updateStatus();
document.getElementById("btn").addEventListener("click", onClick);
#outer {
width: 400px;
height: 20px;
background: #888;
}
#inner {
width: 20px;
height: 20px;
position: relative;
background: red;
}
<div id="outer">
<div id="inner" style="left: 80%"></div>
</div>
<button id="btn">click me</button>
<div id="status"></div>
So, if possible, put your styles inline instead of into CSS because they're easier to access. Technically it is possible to access CSS stylesheet rules using JavaScript but it is very convoluted and I don't recommend it if you're new to web development.

Related

Is there any way to change a div size on scroll using intersectionObserver?

I'm trying to change the size (or scale) of a div while scrolling.
This div has a .8 scale attached to it css. I'd like to reach a scale of 1 progressively while scrolling.
IntersectionObserver seems to be a good choice to work with instead of scroll event but i don't know if i can change the state of an element using it.
You can change the scale of a div using.
document.getElementById("scaledDiv").style.transform = "scale(1)";
The scroll event should do what you want it to do. You can continue to add more if statements and check how many pixels they are scrolling to change it gradually to 1 or even back to 0.8 when they scroll back up. The 50 below represents 50 pixels from the top of the page.
window.onscroll = function() {
if (document.body.scrollTop > 50 || document.documentElement.scrollTop > 50) {
// They are scrolling past a certain position
document.getElementById("scaledDiv").style.transform = "scale(1)";
} else {
// They are scrolling back
}
};
I hope this will help you:
const container = document.querySelector('.container');
const containerHeight = container.scrollHeight;
const iWillExpand = document.querySelector('.iWillExpand');
container.onscroll = function(e) {
iWillExpand.style.transform = `scale(${0.8 + 0.2 * container.scrollTop / (containerHeight - 300)})`;
};
.container {
height: 300px;
width: 100%;
overflow-y: scroll;
}
.scrollMe {
height: 1500px;
width: 100%;
}
.iWillExpand {
position: fixed;
width: 200px;
height: 200px;
top: 10px;
left: 10px;
background-color: aqua;
transform: scale(0.8);
}
<div class='container'>
<div class='scrollMe' />
<div class='iWillExpand' />
</div>

moving an element in JS

I'm learning javascript and trying to make a simple exercise : I have a text box and want control it with keyboard.
My HTML is the following (for now, I'm just trying 1 direction)
const myBox = document.querySelector("h1");
document.addEventListener('keydown', function (event){
if (event.keyCode == '38'){
myBox.style.top -= 5;
console.log("test if it works");
}
});
and my HTML is
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<title>Tuto</title>
<style>
h1 {
width: 200px;
height: 40px;
border: 5px solid #BADA55;
color: #A28;
margin: 0;
text-align: center;
}
</style>
</head>
<body>
<div><h1>My text</h1></div>
<script type="text/javascript" src="main.js"></script>
</body>
</html>
My check test with console log works. So event listener does.
But my box doesn't move. How can I solve it and why my use of .style.top is incorrect ?
Thank you
Positions property like "top", "bottom", "left" and "right" will not work unless your element has the property "position" as "absolute" or "relative".
In that case, what you want is to add "position: relative" to your h1 style on css.
If you want to understand more about that, this can give you a headstart https://www.w3schools.com/css/css_positioning.asp :D
To move an element by changing it's top value, the element can't have a static position (the default). You'll need to change the position to absolute, relative, fixed, etc....
Get the current top, left, etc... using Element#getBoundingClientRect, which will give you the correct initial value, and save you the need to parse a string. Since top needs to have a unit (px, em, etc..), add px to the changed top.
const myBox = document.querySelector("h1");
document.addEventListener('keydown', function(event) {
if (event.keyCode == '38') {
myBox.style.top = myBox.getBoundingClientRect().top - 5 + 'px'; // parse the string to number, subtract 5, and add 'px'
console.log(myBox.style.top);
}
});
h1 {
position: absolute;
top: 0;
left: 0;
width: 200px;
height: 40px;
border: 5px solid #BADA55;
color: #A28;
margin: 0;
text-align: center;
}
<div>
<h1>My text</h1>
</div>
1- you have to use let not const, because you want to change it and it is not fix;
let myBox = document.querySelector("h1");
2- you have to set your element as absolute position. because top property work not in static position
position:absolute;
3- you have to convert value of top position to number and then do something
myBox.style.top = parseFloat(myBox.style.top || 0) - 5 + 'px';
see my code : https://codepen.io/miladfm/pen/dRNLvw

jQuery CSS method update on resize?

After struggling to vertically centre a div inside the body element using the "conventional" methods, I've decided to create a small jQuery function that figures out how far from the top an element needs to be to be "centred".
It works like this:
Get container height,
Get child height,
"top" = "(container.height - child.height) / 2"
Set margin top of child to the value of "top".
For example if the body had a width and height of 1000px and this body had a div.inner child that had a width and height of 400px the margin-top of div.inner would be 300px because (1000-400) / 2 = 300.
Here is a diagram to further explain what I mean:
NOTE: X represents the margin-top of the div.inner (as I didn't have enough space for "Margin Top = ").
To my amazement this actually works!!! Here is the test code:
// set the margin top for ".vertical-centre" elements
$(".vertical-centre").each(function() {
// set the margin-top for the child
$(this).css("margin-top", function() {
// NOTE: margin = (container.height - child.height) / 2
var margin = ($(this).parent().height() - $(this).height()) / 2;
// default the margin to zero if it's a negative number
// round the margin down to the nearest whole number
// specify that the margin-top is in pixels
return Math.floor(Math.max(0, margin)) + "px";
});
});
body {
width: 100px;
height: 100px;
margin: 0;
padding: 0;
border: 1px solid black
}
div.inner {
width: 40px;
height: 40px;
background-color: blue
}
.horizontal-centre {
display: block;
margin-left: auto;
margin-right: auto
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="inner horizontal-centre vertical-centre"></div>
NOTE: I made the example above smaller so you could see it properly.
Unfortunately though, there is now another problem, when I resize the browser the margin-top of the div.inner element stays the same.
I would like for it to be responsive and update it's margin-top property to the appropriate value when the window has been resized otherwise div.inner will go out of view and the page will look a like this:
You could use https://api.jquery.com/resize/
Create a function of your code
function init_center() {..
Try calling the init_center function from the resize event of window
SNIPPET
function init_center() {
// set the margin top for ".vertical-centre" elements
$(".vertical-centre").each(function() {
// set the margin-top for the child
$(this).css("margin-top", function() {
// NOTE: margin = (container.height - child.height) / 2
var margin = ($(this).parent().height() - $(this).height()) / 2;
// default the margin to zero if it's a negative number
// round the margin down to the nearest whole number
// specify that the margin-top is in pixels
return Math.floor(Math.max(0, margin)) + "px";
});
});
}
$( window ).resize(init_center); // Handle resize of window
init_center(); // Doing it first time
body {
width: 400px;
height: 400px;
margin: 0;
padding: 0;
border: 1px solid black
}
div.inner {
width: 200px;
height: 200px;
background-color: blue
}
.horizontal-centre {
display: block;
margin-left: auto;
margin-right: auto
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="inner horizontal-centre vertical-centre"></div>
Wrap you code in a function
function align() {
$(".vertical-centre").each(function() {
// set the margin-top for the child
$(this).css("margin-top", function() {
// NOTE: margin = (container.height - child.height) / 2
var margin = ($(this).parent().height() - $(this).height()) / 2;
// default the margin to zero if it's a negative number
// round the margin down to the nearest whole number
// specify that the margin-top is in pixels
return Math.floor(Math.max(0, margin)) + "px";
});
});
}
And run it on window resize as well
align(); // first run
$(window).on('resize', align); // when window resize

Using outerHeight(); to measure the same class name, but with different heights

The distance of the animation depends on the item0 height. However, when I use a secondary class to change the height of the div, the jQuery does not takes the new height into practice.
Is it possible to factor in the new height, given by the new class name?
http://jsfiddle.net/tmyie/28N7M/1/
jQuery:
var height = $('.item0').outerHeight();
$('p').click(function(){
$('.item0').animate({top:height}, 300);
});
HTML:
<p>click here</p>
<div class="item0"></div>
<div class="item0"></div>
<div class="item0"></div>
<div class="item0 half"></div>
CSS
.item0 {
background-color: red;
height: 50px;
width: 25px;
float: left;
margin-left: 5px;
position: relative;
}
.half {
height: 10px;
background-color: blue;
}
$('p').click(function(){
var $this;
$('.item0').each(function() {
$this = $(this);
$this.animate({
top: $this.outerHeight()
}, 300);
});
});
DEMO
You are first calculating the height, and then defining the click event handler. Obviously you've only calculated the height once and you should recalculate it again. Also you need to calculate it for every item.
Simply calculate the height on click:
$('p').click(function () {
$('.item0').each(function() {
var height = $(this).outerHeight();
$(this).animate({top:height}, 300);
});
});

Animation for the automatic height change when content is resized with javascript

I want to control the automatic height change of the container when I add something that changes the lenght of the content. Right now, if I apply a innerHTML change on the content, the height is changed accordingly. I want to apply a transition to that height change. How can I do that? ( I can also use jQuery )
Record the height before changing the content, change the content, record the height after, set the height to the former height, and animate to the latter height. When the animation has completed, set the height to be automatic again. You can do this using height and animate.
Try it on JSFiddle.
var texts = [
"This is just some sample text that's being used to demonstrate animating the height when content changes.",
"Shorter."
];
var div = $('div').click(changeContent);
function changeContent() {
var oldHeight = div.height();
texts.push(div.text());
div.text(texts.shift());
var newHeight = div.height();
div.height(oldHeight);
div.animate({height: newHeight}, 'fast', function() {
div.height('auto');
});
}
div {
width: 150px;
background: lightgray;
overflow-y: hidden;
}
<div>This is some example content.</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<div id="containter" style="overflow:hidden">
<div>
Content.....
</div>
</div>
//add something...
$('#container').animate({height:$('#container').content().outerHeight()});
or:
$('#container').animate({height:$('#container').children().first().outerHeight()});
and when adding append to the div inside the containter:
$('#container').children().first().append(somethingNew);
Based on icktoofay's answer.
I make the button disabled while changing the height and add a fading effect. This solution is useful for updating of the products filter and so on.
Also I check the box-sizing property. If it's box-sizing then I get newHeight by .outerHeigth() instead of .height() to prevent the height fluctuation when new content has the same height. You can check this situation, for example by setting the random variable to value 5. The reason is that
.height() will always return the content height, regardless of the value of the CSS box-sizing property.
CodePen
$('#button').click(function() {
var $button = $(this),
buttonOriginalText = $button.html();
$button.prop('disabled', true).html('Updating...');
$('#content').animate({
opacity: 0
}, 'fast', function() {
var newHeight,
$content = $(this),
oldHeight = $content.height();
$content.html(getRandomContent());
newHeight = ('border-box' === $content.css('box-sizing') ? $content.outerHeight() : $content.height());
$content.height(oldHeight).animate({
height: newHeight,
opacity: 1
}, 'slow', function() {
$content.height('auto');
$button.prop('disabled', false).html(buttonOriginalText);
});
});
});
function getRandomContent() {
var random = 1 + Math.round(Math.random() * 11), // 1..12
paragraph = '<p>Paragraph</p>';
return paragraph.repeat(random);
}
* {
box-sizing: border-box; /* comment out to test "content-box" */
font: 16px Helvetica, 'sans-serif';
}
.content {
counter-reset: content;
padding: 6px 18px;
}
.content p {
counter-increment: content;
}
.content p:after {
content: ' ' counter(content) '.';
}
.content-box {
border: 2px solid red;
margin-top: 24px;
max-width: 220px;
}
<button id="button" class="button">Update the content</button>
<div class="content-box">
<div id="content" class="content">Animatie the automatic height when content is resized.</div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>

Categories