Reputation: 2777
I am trying to create "Toast" messages in webpages like in Android. I am done with the creation and styling, but the only problem is the transition. I want the toast to fade in and fade out. This is the code I wrote so far :
function show(msg = "Hello") {
var t = document.getElementById("toast");
t.innerHTML = msg;
t.style.display = "flex";
t.style.opacity = 1;
setInterval(function() {
t.style.opacity = 0;
}, 2000);
}
* {
box-sizing: border-box;
}
html,
body {
width: 100%;
height: 100%;
padding: 10px;
margin: 0;
}
#toast {
background-color: black;
color: white;
opacity: 0;
border-radius: 0.7em;
position: fixed;
top: 50%;
left: 50%;
transform: translateX(-50%) translateY(-50%);
padding: 10px;
display: none;
text-align: center;
align-items: center;
justify-content: center;
transition: opacity 1s;
}
<html>
<head>
<title>Toast demo</title>
</head>
<body>
<div id="toast"></div>
<button onclick="show('Message')">Show toast</button>
<button onclick="show('Message<br>with<br>multiple<br>lines')">Show toast</button>
</body>
</html>
With this code, at the first instance, the fade-in is not there, and the subsequent ones are shown for a small time interval. Why does this happen and how to fix this behaviour? CSS solution is appreciated and I do not want to use jQuery.
Upvotes: 1
Views: 252
Reputation: 2777
I came up with a complete idea which can show as well as disappear the toast after a specified time and also, it works very smoothly even if the toast is invoked when it is already displaying. Here is my code:
var t = document.getElementById("toast");
var showing = false;
var timeout1, timeout2;
function showToast(msg = "") {
clearTimeout(timeout1);
clearTimeout(timeout2);
t.innerHTML = msg;
if (!showing) {
showing = true;
t.style.display = "flex";
t.animate({
opacity: ["0", "1"]
}, {
duration: 1000,
iterations: 1,
fill: "forwards"
});
}
timeout1 = setTimeout(() => {
showing = false;
t.animate({
opacity: ["1", "0"]
}, {
duration: 1000,
iterations: 1,
fill: "forwards"
});
}, 3000);
timeout2 = setTimeout(() => {
t.style.display = "none";
}, 4000);
}
* {
box-sizing: border-box;
}
#toast {
background-color: black;
color: white;
border-radius: 0.7em;
position: fixed;
left: 50%;
top: 50%;
opacity: 0;
display: none;
transform: translateX(-50%) translateY(-50%);
padding: 10px;
text-align: center;
align-items: center;
justify-content: center;
}
button {
width: 100%;
height: 50%;
}
<div id="toast"></div>
<button onclick="showToast('Hello')">Show toast</button>
<button onclick="showToast('Hi')">Show toast</button>
Any more suggestion is appreciated.
Upvotes: 0
Reputation: 383
instead of:
function show(msg = "Hello") {
var t = document.getElementById("toast");
t.innerHTML = msg;
t.style.display = "flex";
t.style.opacity = 1;
setInterval(function() {
t.style.opacity = 0;
}, 2000);
}
You can use Vanilla Javascript new .animate() Api, which is more performant than both setInterval & RequestAnimationFrame():
var t = document.getElementById("toast");
t.animate({
filter: ["opacity(1)","opacity(0)"]; // Start & End States of the Animation.
},{
duration: 488, // Duration in Ms
fill: 'forwards', // retains the end State of the animation.
iterations: 1, // Number of iterations or Infinity
delay: 88, // Delay for the Animation Start (2000)
easing: 'ease-in', // Easing Function
// direction:,
// endDelay:,
// iterationStart:,
});
This Also Gives you alot more control than Just pure Css Animations & better matches the browsers refresh/repaint Cycles. More information can be found here MDN WebAnimation Api
If you want this to work via touch or mouseclick events then you need to add in the appropriate event handlers to deal with this.
https://developer.mozilla.org/en-US/docs/Web/API/Touch_events
You mentioned that the code above is not working, probably because it doesn't have any event listeners attached to is so I've made an update.
HTML::
<html>
<head>
<title>Toast demo</title>
</head>
<body>
<div id="toast"></div>
<button id="ShowMsg">Show toast</button>
<button id="ShowMsg2">Show toast</button>
<script src="LinkToYourJsFile.js">Or Include The Js In Here..</script>
</body>
</html>
JS::
let ShowMsg = document.getElementById("ShowMsg");
let ShowMsg2 = document.getElementById("ShowMsg2");
function showToast(){
var t = document.getElementById("toast");
t.innerHTML='<p>Message you want to display</p>'; // For multiline, just repeat with <br> or Js equivelent \n
t.animate({
filter: ["opacity(0)","opacity(1)"] // Start & End States of the Animation.
},{
duration: 488, // Duration in Ms
fill: 'forwards', // retains the end State of the animation.
iterations: 1, // Number of iterations or Infinity
delay: 88, // Delay for the Animation Start (2000)
easing: 'ease-in', // Easing Function
// direction:,
// endDelay:,
// iterationStart:,
});
}
ShowMsg.addEventListener("mousedown", showToast); // 1) What is the event, 2) name of the function to run when the event occurs
ShowMsg2.addEventListener("mousedown", showToast2StarvinMarvin); // Repeat the same process for toast 2.
** Note that in your Css your t => toast Msg should intially start with filter:opacity(0); and not have display:none; as in your original code. Javascript will over-ride this when the events are fired. also the Js MUST either be at the bottom of the Html document OR be in an external file linked at the bottom of the Html. or alternatively wrapped inside of
document.addEventListener("DOMContentLoaded", (function(){
// Your Anime code & variables etc goes here;
}));
To fade the element out repeat but change the event listener to "mouseleave" and switch the opacity value in .animate() function around. So 0 = 1, 1 = 0;
I'm still learning this stuff myself so see if you can read some of the documentaion on Mozilla Developer Network & here on Stack Overflow to get it working how you would like.. Hope this helps.
Upvotes: 1