I am dynamically creating divs that have a height of 0% which is specified in the css rule.
I then have an option/select menu that has an event listener listening for a change. When this change is applied, I want it to apply a height of 75%, which works fine.
Though what I also want it to do is to transition to this new height with a duration of 2 seconds. This transition is specified in the css rule for the element.
I cannot make this work however, it goes straight to the full 75% height, no transition.
I understand that I need to make the code 'live' in the DOM, so I have tried separating the appending and the application of height style in two different functions.
function makeBar() {
container.innerHTML = '';
let element = document.createElement('div');
element.classList.add('bar');
element.setAttribute('class', 'bar');
element.style.backgroundColor = select.value;
container.appendChild(element);
}
select.addEventListener('change', function() {
makeBar();
})
select.addEventListener('change', function() {
let bar = document.getElementsByClassName('bar')[0];
bar.style.height = '75%';
})
It works fine however if I were to replace my second function which applies the height with an event listener that listens out for a click on a button. Please see https://jsfiddle.net/tdmitchell/3eu48zw7/30/ to see what I mean.
Though what I actually want to happen is for the bar to smoothly transition from 0% to its new height of 75% when the dropdown menu is changed.
All the 'results' of the browser's render process are occurring at the same time - all at once. You must tell the browser to wait until the next rendering cycle before 'growing' the element.
window.requestAnimationFrame(() => {
bar.style.height = '75%';
});
Will accomplish this.
Related
When you scroll on a page, the page shows an element, I want to be able to specify this element and
do code with it using JS, is this possible? I tried something but it had another problem..
What I tried was,
let section = document.getElementById('out');
window.onscroll = function() {
if (window.scrollY >= 678) {
document.getElementById('out').style.color = "red";
} else {
document.getElementById('out').style.color = "black"
}
}
I didn't use animate here, I just made sure it works, and it did, well almost, because if you zoom in/out it ruins it, I think that's because I got the 678 by going to the button and printing scrollY manually, is there anyway to make that automatic, so it works on any element I need?
I searched a lot and can't seem to find what I need, the solutions need jQuery, I need a solution only with html, css, and javascript.
In the future the solution will be css scroll timelines, but as that feature is at the time of writing experimental and is not supported by major browsers you can use intersection observers.
Quoted from MDN:
The Intersection Observer API lets code register a callback function that is executed whenever an element they wish to monitor enters or exits another element (or the viewport), or when the amount by which the two intersect changes by a requested amount.
To animate a component when it is in or out of view, you can give animated elements a .hidden class in your html markup and create an intersection observer which appends the .shown class to .hidden elements when they are in view.
const observer = new IntersectionObserver(entries => {
entries.forEach(entry => entry.target.classList.toggle(“shown”, entry.isIntersecting))
})
const hiddenElements = document.querySelectorAll(“.hidden”)
hiddenElements.forEach((el) => observer.observe(el))
Then you can just apply transitions under a <selector>.shown css rule.
For a website I made a JS, to animate a div upon scroll. I was animating the CSS top value, but since I had a transition applied to that property, I had to come up with a way of animating "top" without the lag that CSS transitions results in.
I came up with this inside a function:
//Get the Div Element
var div = document.querySelector('.someclass');
//Disable Transition
div.style.transition= 'none';
//Change the top value - value was connected to scroll
div.style.top = anumber + 'px';
//Reset Transition to the CSS's file default
div.style.transition = '';
But this resulted in the unwanted lag again, because it somehow ignored the "Disable Transition" step.
To make sure that each step would be executed, I came up with the Idea to wrap the "Reset" step into a setTimeout function. I changed this line:
//Reset Transition to the CSS's file default
div.style.transition = '';
to
//Reset Transition to the CSS's file default
setTimeout(function () {
div.style.transition = '';
},1);
And Tadaa, it worked. But now I'm wondering, if there was a cleaner way to prevent the first line of not being executed, and of course an explenation why it even happened.
I appreciate all the help!
Changes to the DOM only get reflected to the underlying model when the CSS engine runs, which only happens if JS stopped running. By using setTimeout the execution of JS ends, the CSS engine has time to run, then the timer fires and JS runs again.
You could solve ut a bit more elegantly with:
const tick = () => new Promise(resolve => setTimeout(resolve));
(async function() {
//Get the Div Element
var div = document.querySelector('.someclass');
//Disable Transition
div.style.transition= 'none';
//Change the top value - value was connected to scroll
div.style.top = anumber + 'px';
await tick();
//Reset Transition to the CSS's file default
div.style.transition = '';
})();
I have 2 menus that are contained on the same page but in different locations. One appears when I have screen width > 555px and the parent div is set as display:none when under this size. The other is display:none when 555px or over and shows when less than 555px. Its the same menu but one is on a different section of the page for mobile/smaller screens.
I have javascript on some of the drop down options on the menu and it works for the first menu but not the second, I believe because the js is still attempting to run for the display:none so on the second attempt (on the small screens) it doesn't work.
I'm probably over-complicating what needs to be done but I've attempted to create some code that uses js to create the div that contains the menu code for desktop and mobile and removes them when it shouldn't... so a media query version controlled by js rather than css that rather than hide the div and content will actually make it not be present.
I'm a complete novice at javascript and have just attempted to adapt other code I've seen, basically all I want to do is when >555px browser width add and when it moves under remove then I can use the same code down the page to add/remove div id="y"...
function DynamicDiv() {
var dynDiv = document.createElement("div");
dynDiv.id = "search-holder1";
dynDiv.innerHTML = "Created using JavaScript";
document.body.appendChild(dynDiv);
}
var elem = document.getElementById("search-holder1");
function myFunction(x) {
if (x.matches) { // If media query matches
DynamicDiv();
} else {
elem.parentNode.removeChild(elem);
}
}
var x = window.matchMedia("(max-width: 555px)")
myFunction(x) // Call listener function at run time
x.addListener(myFunction) // Attach listener function on state changes
Here is my way:
function chooseMenu() {
var width = window.innerWidth;
if (width > 555) {
//Add your div
else {
//If div is in document then remove, and add mobile div
}
}
window.onresize = chooseMenu;
You should probably do the same function at the beggining with an anonymous function too.
So I have an element that is using CSS3 transitions to move across the page. I'm trying to see how the actual output FPS of that animation on the page is (for instance, if the page is outputting at 5FPS, a div moving from 0px to 10px at a transition value of 1s should report back 2px, 4px, 6px, etc).
Instead, I just get whatever value I already set the div's position to.
// css has defined a transition of 10s on the moving div
document.getElementById("movingDiv").style.left = "0px";
console.log(document.getElementById("movingDiv").style.left); //outputs 0px
document.getElementById("movingDiv").style.left = "100px";
window.setTimeout(function(){
console.log(document.getElementById("movingDiv").style.left); //outputs 100px instead of, for instance, 43px or wherever the div would visually appear to be
}, 3000);
That's not the exact code, but just some that's generic enough to illustrate my point.
Restating the question, how would I find where an element visually appears to be during its transition between one position and another? I'm not using jQuery animations as many others have answered for, and don't just want to calculate where the element should be. I want to see where the element actually appears to be on the page. I would also like if this works off-screen as well (like to the left of or above the visible window).
To help see why I'm actually trying to do this, is that I'm trying to get the FPS output of the page. I have seen many cases where the page outputs terrible FPS but Javascript still outputs over 100 FPS because the Javascript can run faster than the page can render itself which I'm trying to avoid.
You can use window.requestAnimationFrame:
var moving = false,
el = document.getElementById("mover");
el.className = el.className + " move-right";
el.addEventListener('transitionend', function () {
moving = true;
});
function getPosition() {
var rect = el.getBoundingClientRect()
console.log(rect.top, rect.left);
if (!moving) {
window.requestAnimationFrame(getPosition);
}
}
window.requestAnimationFrame(getPosition);
http://jsfiddle.net/ob7kgmbk/1/
I've thrown together a cool little script that will make my search box appear using jQuery UI. However, there are links above the search box that must move up at the same speed as well. For this, the margin-top must be adjusted, but by toggling the margin-top, it seems it is disappearing.
Does anyone know how I can toggle the margin-top without making the links disappear AND keep the speed as close as possible to the other one?
$(document).ready(function() {
$('.pwcustomsearch').hide();
$("#pwcustomsearchlink").click(function () {
var effect = 'slide';
var options = { direction: 'down' };
var duration = 400;
$('.pwcustomsearch').toggle(effect, options, duration);
$('.social-media').toggle({"marginTop": "15px"});
})
});
Here is a fiddle:
http://jsfiddle.net/hcmLw/1030/
.toggle() is adding display:none as an inline style to your element, therefore it disappears.
Use .animate() instead to change the top margin.
See my updated fiddle here: http://jsfiddle.net/hcmLw/1032/
EDIT: Updated the fiddle again to make the toggling work properly.