I have an element whose CSS I want to change when the user mouses in or out. Some functions to trigger that, simple enough:
function expandDiv(){
div1.classList.add('expanded')
}
function shrinkDiv(){
div1.classList.remove('expanded')
}
However, I want the user to be hovered over (or away from) that element for a quick moment (say 200ms) before the function actually triggers. This is for usability, as a quick pass over the element shouldn't trigger the function. This issue was discussed here, and I liked this solution:
var delay = function (elem, callback) {
var timeout = null;
elem.onmouseover = function() {
// Set timeout to be a timer which will invoke callback after 1s
timeout = setTimeout(callback, 1000);
};
elem.onmouseout = function() {
// Clear any timers set to timeout
clearTimeout(timeout);
}
};
But this solution only works in one direction when the callback function is my expandDiv()
or shrinkDiv()
function. I tried simply writing 2 functions with the delay()
structure and reversing the onmouseout
and onmouseenter
events:
// Delay functions
var delayIn = function (elem, callback) {
var timeout = null;
elem.onmouseover = function() {
// Set timeout to be a timer which will invoke callback after 1s
timeout = setTimeout(callback, 1000);
};
elem.onmouseout = function() {
// Clear any timers set to timeout
clearTimeout(timeout);
}
};
var delayOut = function (elem, callback) {
var timeout = null;
elem.onmouseout = function() {
// Set timeout to be a timer which will invoke callback after 1s
timeout = setTimeout(callback, 200);
};
elem.onmouseover = function() {
// Clear any timers set to timeout
clearTimeout(timeout);
}
};
delayIn(div1, expandDiv);
delayOut(div1, shrinkDiv);
But this is not working. When both delayIn and delayOut are called, the delayIn function does nothing. See this codepen:
https://codepen.io/slutske22/pen/eYYzQqE?editors=0010
I'm not sure whats wrong. The div should expand after a second of hovering over it, unless you mouseout before the second is over. But it does nothing. I added a "Expand the Div" button to force it, and you can see that if you then mouseover the div and mouseout, it does indeed shrink as it should from the shrinkDiv function, but there is no delay as there should be from the delayOut function.
I am also trying to link this behavior to a button. There is a "Shrink the div" button. The desired behavior is that you click the button, and the div will shrink after the defined setTimout delay, unless you mouseenter the div before the timeout finishes. This is not happening either.
I feel like this is a relatively straightforward task but its eluding me. I'd like to learn how to do it without jQuery. Also I realize this should probably be a commend in this thread, but I'm new and can't comment yet. Sorry if this is bad form.
the onmouse* handlers you assign in delayIn
are overwritten in delayOut
- which is why only one of the "animations" does what it should
var div1 = document.querySelector('#div1')
var button = document.querySelector('#button')
function expandDiv(){
div1.classList.add('expanded')
}
function shrinkDiv(){
div1.classList.remove('expanded')
}
var delayInOut = function(elem, incb, outcb) {
var timeout = null;
var clearto = function() {
clearTimeout(timeout);
timeout = null;
};
elem.onmouseover = function() {
clearto();
timeout = setTimeout(incb, 1000);
}
elem.onmouseout = function() {
clearto();
timeout = setTimeout(outcb, 200);
}
}
delayInOut(div1, expandDiv, shrinkDiv);
expandButton.onclick = expandDiv
shrinkButton.addEventListener("click", function(){
var timeout = setTimeout(shrinkDiv, 1000);
}, false)
body{
position: relative;
padding: 0px;
margin: 0px;
}
#div1{
position: absolute;
top: 20px;
left: 20px;
right: 20px;
height: 50px;
background-color: darkblue;
transition: height 1s;
}
#div1.expanded{
height: 70px;
}
#expandButton{
position: absolute;
left: 20px;
top: 100px;
}
#shrinkButton{
position: absolute;
left: 20px;
top: 125px;
}
<body>
<div id="div1"></div>
<button id="expandButton">Expand Div</button>
<button id="shrinkButton">Shrink Div After Delay</button>
</body>
As a point of interest, you can achieve this without javascript, including having different delays for expanding and contracting:
body{
position: relative;
padding: 0px;
margin: 0px;
}
#div1{
position: absolute;
top: 20px;
left: 20px;
right: 20px;
height: 50px;
background-color: darkblue;
transition: height 1s ease-in-out 200ms;
}
#div1:hover{
transition-delay:1s;
height: 70px;
}
<body>
<div id="div1"></div>
</body>