Many transitions in Bootstrap 4 provide a set of events to listen for. For example, you could do something like:
$('.certainDropdowns').on('hidden.bs.dropdown', function() {
// do the things
});
A light inspection of some of the components shows that somehow they are able to respond to fading. For example, the Bootstrap modal fires a "hidden" event once it has faded out. But this is at the modal level, not the transition level (hidden.bs.modal)
Unlike dropdowns and modals, there is not a "fade" JavaScript component. But the light scan of the source code seems to be indicating that Bootstrap provides emulation for CSS transitionEvent, and I'm trying to figure out how I can tap into it.
In brief:
Is there a Bootstrap 4-provided method for tapping into the fade transition's events, or am I limited to native transitionend (possibly with help from a 3rd-party polyfill)?
[edit to add content below]
I possibly should have tried transitionend before posting the question, but I just gave it a try and it seems to be no go like this:
<div id="something" class="fade show">Fadeable</div>
Then JS:
$('#something').on('transitionend', function() {
console.log('transition ended!');
});
//later
$('#something').removeClass('show');
This was tested only with the latest Firefox, which is one of my target browsers.
I couldn't find a way to do it in my intended way with the provided components. Instead, I ended up writing it as a single new class, "collapseFade" which could still use the Bootstrap pattern of adding/removing the class "show".
The tricky thing was that transitions would trample over each other if I just tried to add or remove the "show" class, so I had to add a second helper class, "out". This requires intimate understanding of the new classes, which was potentially hazardous to maintenance developers. Consequently, I wrote a jQuery plugin to go with it. Without using this answer as code repository, here's the lightweight breakdown:
SASS:
.collapseFade {
max-height: 400px;
transition:
max-height 0.5s,
opacity 0.5s 0.5s;
&:not(.show) {
opacity: 0;
max-height: 0;
}
}
.collapseFade.out {
transition:
max-height 0.5s 0.5s,
opacity 0.5s;
&:not(.show) {
opacity: 0;
max-height: 0;
}
}
(You could theoretically use Bootstrap's SASS fade variables instead of hard-coding time intervals).
Then the plug-in (code not included, for brevity) simply allows you to call collapseFade on an element. Eg. $('.something').collapseFade(). It optionally accepts "show" or "hide" as string parameters, but will just toggle by default. For whatever reason, transitionend is working here, so I also listen and fire an appropriate custom event for future maintenance or feature devs who might find it useful.
It functions thus: when showing, it removes the out class and adds the show class. When hiding, it adds the out class and then removes the show class.
The so-called "magic" is just in the timing. The second transition start is delayed by a value equal to the first transition time, which visually chains them together even though technically they are fired at the same time.
The other tricky bit is that the collapse animates max-height rather than height. This is the way Bootstrap themselves do it, and it makes sense... you can't animate "auto" height; it needs an actual target number. But straight-up "height" (no "max") means you're committed to occupying a certain amount of space. Max-height will allow height to be dynamic, but the trade-off is that it will operate smoothly only by restricting it as closely as possible. If I had put max-height of ten-thousand, for example, the collapsing animation wouldn't be smooth. You might notice that in Bootstrap's own collapse functionality, which is less than smooth for elements that are not tall. I don't anticipate my targets to be any taller than 400px so that's what I've provided.
Related
I am trying to understand the DOM through JS.
When I click on the button (named collapse all, I want the section below to be collapsed (disappear), but slowly and smoothly ( a transition?), and then when I click again on the same button, I want it to appear again, and so forth...
how can I achieve this through JS?
I wrote this code, but still not working repeatedly , it is working only once :
btn_all.addEventListener("click", funct1);
function funct1(e) {
section2.style.transition = "all ease 5s";
section2.style.display = "none";
for (let i = 0; i < 100; i++) {
btn_all.addEventListener("click", funct2);
function funct2(e1) {
section2.style.display = "block";
}
}};
the transition is not working...
display: none is instant - you'll want to change some attribute (like height or opacity) and then set display: none when it's done, and put the transition in the base style.
Also, accessibility guidelines (and good taste) generally state that transitions should be fast - give the impression of motion, without making them sit through an animation. 250ms is a pretty good speed
E.g.
.section {
transition: opacity ease-in .25s;
}
.section.fade-out {
opacity: 0;
}
Also, jquery has some simple transitions that generally work unless you're going supernuts in the styles, if that is available. e.g.
$(".section2").fadeOut(250);
It negates the need for a lot of nullchecking, browser compatibility workarounds, etc. and has a lot of easy shortcuts. Great for quick development and easy to approach for beginner developers.
As jQuery.fadeIn is not very smooth on mobile devices I try to use CSS but it doesn't work as expected. How to create a smooth CSS animation using Javascript?
In general this is what I'm trying:
$('div')
.css('opacity', 0) // at first, set it transparent
.css('display', 'block') // make it appear
.css('transition', 'opacity 1000ms linear') // set a transition
.css('opacity', 1); // let it fade in
https://jsfiddle.net/8xa89y04/
EDIT1:
I'm not searching a solution using static CSS classes. The point is: I need to set this dynamically in Javascript code - a replacement for jQuerys fadeIn() for example.
Your logic isn't quite right. Firstly you cannot animate display, so to achieve what you require the element has to always be rendered in the DOM (ie. anything but display: none). Secondly, the transition property should be placed within the CSS styling itself. Finally you can make this much more simple by setting all the rules in CSS classes and just turning the class on/off. Try this:
div {
position: absolute;
width: 100px;
height: 100px;
background-color: black;
opacity: 0;
transition: opacity 1000ms linear;
}
.foo {
opacity: 1;
}
$('div').addClass('foo');
Working example
Use this code.
CSS
div {
width: 100px;
height: 100px;
background-color: black;
transition:opacity 2s;
}
JavaScript
$('div').hover(function(){
$(this).css('opacity','0');
})
Without using CSS properly, you are going the long way about it. You'll need to emulate what you would normally do in CSS, using JavaScript, so you'll be setting all your CSS properties, transitions etc, then applying them with js.
I can't personally see any benefit in doing this. Using actual CSS would be cleaner, more efficient, more maintainable, and simply a plain better solution to what you need.
I think this is what you are looking for.
$('div').css({"display":"block", "opacity":"0"}) //Make div visible and opacity as "0"
$('div').animate({opacity :1}, 1000); //Animate div to opacity "1"
Take a look at this Demo
Found the cause here: CSS transitions do not work when assigned trough JavaScript
To give this attention I need to give the browser some time - or better: a working slot to activate the transition as the time seems not to be a problem.
The following code cuts the process in two by using setTimeout()... and it works!
var div = $('div');
// first process
div
.css('opacity', 0) // initial opacity
.css('display', 'block') // make it appear (but still transparent)
.css('transition', 'opacity 1s linear'); // set up a transition for opacity
// break - start the transition in a new "thread" by using setTimeout()
window.setTimeout(function(){
div.css('opacity', 1); // start fade in
}, 1); // on my desktop browser only 1ms is enough but this
// may depend on the device performance
// maybe we need a bigger timeout on mobile devices
Using ng-animate to provide some transitions when a user clicks into a tab.
Simply using
.ng-enter{
transition:0.50s;
opacity: 0;
}
.ng-enter-active{
opacity: 1;
}
which works a treat. However, the first time you click in it's somewhat sticky.
The view which is being transitioned into displays for a brief second before being transitioned into, so you see it twice almost. This only occurs on the first time round, on subsequent visits the transition works perfectly.
Is there a way then to make the animation smoother the first time round? The tabs are being displayed using a div with ng-include.
You have to target the animation on the tab elements, like this :
.tab.ng-enter {
transition:0.5s linear all;
opacity:1;
}
.tab.ng-enter.ng-enter-active {
opacity:0;
}
ngAnimate (like ngLeave, ngEnter, etc...) classes will be added to each element appearing/disappearing in the view, so in your case, it is needed to restrain your animation to only one element.
You can see the documentation too.
I have a dialog in which I'd like to display one of two things depending on the state of a variable. So, I hooked up 2 versions of a form with ng-if.
When you click "delete" button on first state, it toggles to the second state.
I wanted to make it less abrupt, so I tried adding some css:
[ng-if].ng-enter {
animation: fadeIn .5s;
}
[ng-if].ng-leave {
animation: fadeOut .5s;
}
These animations come from the bower package "animate css":
#keyframes fadeIn {
0% {opacity: 0;}
100% {opacity: 1;}
}
.fadeIn {
animation-name: fadeIn;
}
However, as you can see in my animated GIF below, what happens is that for a second BOTH forms appear, making the dialog taller, then one fades out.
Is there no way to do a simple fadein/fadeout as in jQuery? I used to do this all the time with it, but trying to get nice UI animation in Angular is eluding me.
I had a similar problem with an Angular app and animations. I ended up having to use jquery - I wish I had a better answer - but it turned out beautifully. One note, though, I had to wrap any jquery I used in a noConflict() and use body on click plus the element because it doesn't exist yet in the DOM:
$.noConflict();jQuery( document ).ready(function( $ ) {
$('body').on('click', "#toggler", function(){
$('#div_to_show').slideDown();
});
});
I realize this a tangential answer and not an elegant solution but it worked for me to get it out the door under a tight deadline.
A clean solution is to change the state you use to check which form is to display when the animation ng-leave ends.
You can use a second variable to set the ng-leave class in the form that will be hidden.
I can't post you some code because i don't know your js and html.
I have a DOM element with this effect applied:
#elem {
transition: height 0.4s ease;
}
I am writing a jQuery plugin that is resizing this element, I need to disable these effects temporarily so I can resize it smoothly.
What is the most elegant way of disabling these effects temporarily (and then re-enabling them), given they may be applied from parents or may not be applied at all.
Short Answer
Use this CSS:
.notransition {
-webkit-transition: none !important;
-moz-transition: none !important;
-o-transition: none !important;
transition: none !important;
}
Plus either this JS (without jQuery)...
someElement.classList.add('notransition'); // Disable transitions
doWhateverCssChangesYouWant(someElement);
someElement.offsetHeight; // Trigger a reflow, flushing the CSS changes
someElement.classList.remove('notransition'); // Re-enable transitions
Or this JS with jQuery...
$someElement.addClass('notransition'); // Disable transitions
doWhateverCssChangesYouWant($someElement);
$someElement[0].offsetHeight; // Trigger a reflow, flushing the CSS changes
$someElement.removeClass('notransition'); // Re-enable transitions
... or equivalent code using whatever other library or framework you're working with.
Explanation
This is actually a fairly subtle problem.
First up, you probably want to create a 'notransition' class that you can apply to elements to set their *-transition CSS attributes to none. For instance:
.notransition {
-webkit-transition: none !important;
-moz-transition: none !important;
-o-transition: none !important;
transition: none !important;
}
Some minor remarks on the CSS before moving on:
These days you may not want to bother with the vendor-prefixed properties like -webkit-transition, or may have a CSS preprocessor that will add them for you. Specifying them manually was the right thing to do for most webapps when I first posted this answer in 2013, but as of 2023, per https://caniuse.com/mdn-css_properties_transition, only about 0.4% of users in the world are still using a browser that supports only a vendor-prefixed version of transition.
There's no such thing as -ms-transition. The first version of Internet Explorer to support transitions at all was IE 10, which supported them unprefixed.
This answer assumes that !important is enough to let this rule override your existing styles. But if you're already using !important on some of your transition rules, that might not work. In that case, you might need to instead do someElement.style.setProperty("transition", "none", "important") to disable the transitions (and figure out yourself how to revert that change).
Anyway, when you come to try and use this class, you'll run into a trap. The trap is that code like this won't work the way you might naively expect:
// Don't do things this way! It doesn't work!
someElement.classList.add('notransition')
someElement.style.height = '50px' // just an example; could be any CSS change
someElement.classList.remove('notransition')
Naively, you might think that the change in height won't be animated, because it happens while the 'notransition' class is applied. In reality, though, it will be animated, at least in all modern browsers I've tried. The problem is that the browser is buffering the styling changes that it needs to make until the JavaScript has finished executing, and then making all the changes in a single "reflow". As a result, it does a reflow where there is no net change to whether or not transitions are enabled, but there is a net change to the height. Consequently, it animates the height change.
You might think a reasonable and clean way to get around this would be to wrap the removal of the 'notransition' class in a 1ms timeout, like this:
// Don't do things this way! It STILL doesn't work!
someElement.classList.add('notransition')
someElement.style.height = '50px' // just an example; could be any CSS change
setTimeout(function () {someElement.classList.remove('notransition')}, 1);
but this doesn't reliably work either. I wasn't able to make the above code break in WebKit browsers, but on Firefox (on both slow and fast machines) you'll sometimes (seemingly at random) get the same behaviour as using the naive approach. I guess the reason for this is that it's possible for the JavaScript execution to be slow enough that the timeout function is waiting to execute by the time the browser is idle and would otherwise be thinking about doing an opportunistic reflow, and if that scenario happens, Firefox executes the queued function before the reflow.
The only solution I've found to the problem is to force a reflow of the element, flushing the CSS changes made to it, before removing the 'notransition' class. There are various ways to do this - see here for some. The closest thing there is to a 'standard' way of doing this is to read the offsetHeight property of the element.
One solution that actually works, then, is
someElement.classList.add('notransition'); // Disable transitions
doWhateverCssChangesYouWant(someElement);
someElement.offsetHeight; // Trigger a reflow, flushing the CSS changes
someElement.classList.remove('notransition'); // Re-enable transitions
Here's a JS fiddle that illustrates the three possible approaches I've described here (both the one successful approach and the two unsuccessful ones):
http://jsfiddle.net/2uVAA/131/
Add an additional CSS class that blocks the transition, and then remove it to return to the previous state. This make both CSS and JQuery code short, simple and well understandable.
CSS:
.notransition {
transition: none !important;
}
Note: !important was added to be sure that this rule will have higher preference, because using an ID is more specific than class.
JQuery:
$('#elem').addClass('notransition'); // to remove transition
$('#elem').removeClass('notransition'); // to return to previouse transition
I would advocate disabling animation as suggested by DaneSoul, but making the switch global:
/*kill the transitions on any descendant elements of .notransition*/
.notransition * {
transition: none !important;
}
.notransition can be then applied to the body element, effectively overriding any transition animation on the page:
$('body').toggleClass('notransition');
For a pure JS solution (no CSS classes), just set the transition to 'none'. To restore the transition as specified in the CSS, set the transition to an empty string.
// Remove the transition
elem.style.transition = 'none';
// Restore the transition
elem.style.transition = '';
If you're using vendor prefixes, you'll need to set those too.
elem.style.webkitTransition = 'none'
You can disable animation, transition, transforms for all of element in page with this CSS code:
var style = document.createElement('style');
style.type = 'text/css';
style.innerHTML = '* {' +
' transition-property: none !important;' +
' transform: none !important;' +
' animation: none !important;}';
document.getElementsByTagName('head')[0].appendChild(style);
I think you could create a separate CSS class that you can use in these cases:
.disable-transition {
transition: none;
}
Then in jQuery you would toggle the class like so:
$('#<your-element>').addClass('disable-transition');
If you want a simple no-jquery solution to prevent all transitions:
Add this CSS:
body.no-transition * {
transition: none !important;
}
And then in your js:
document.body.classList.add("no-transition");
// do your work, and then either immediately remove the class:
document.body.classList.remove("no-transition");
// or, if browser rendering takes longer and you need to wait until a paint or two:
setTimeout(() => document.body.classList.remove("no-transition"), 1);
// (try changing 1 to a larger value if the transition is still applying)
This is the workaround that worked easily for me. It isn't direct answer to the question but still may help someone.
Rather than creating notransition class which was supposed to cancel the transition
.notransition {
-webkit-transition: none !important;
-moz-transition: none !important;
-o-transition: none !important;
transition: none !important;
}
I created moveTransition class
.moveTransition {
-webkit-transition: left 3s, top 3s;
-moz-transition: left 3s, top 3s;
-o-transition: left 3s, top 3s;
transition: left 3s, top 3s;
}
Then I added this class to element with js
element.classList.add("moveTransition")
And later in setTimeout, I removed it
element.classList.remove("moveTransition")
I wasn't able to test it in different browsers but in chrome it works perfectly
If you want to remove CSS transitions, transformations and animations from the current webpage you can just execute this little script I wrote (inside your browsers console):
let filePath = "https://dl.dropboxusercontent.com/s/ep1nzckmvgjq7jr/remove_transitions_from_page.css";
let html = `<link rel="stylesheet" type="text/css" href="${filePath}">`;
document.querySelector("html > head").insertAdjacentHTML("beforeend", html);
It uses vanillaJS to load this css-file. Heres also a github repo in case you want to use this in the context of a scraper (Ruby-Selenium): remove-CSS-animations-repo
does
$('#elem').css('-webkit-transition','none !important');
in your js kill it?
obviously repeat for each.
I'd have a class in your CSS like this:
.no-transition {
-webkit-transition: none;
-moz-transition: none;
-o-transition: none;
-ms-transition: none;
transition: none;
}
and then in your jQuery:
$('#elem').addClass('no-transition'); //will disable it
$('#elem').removeClass('no-transition'); //will enable it