from :hover to a togglebutton in react - javascript

What I am trying to do:
I have a card that when hovered over expands and does a bunch of different animations i worked on. this is great for desktop and not mobile. So when the screen is a certain res I make a toggle button visible. I want this button in react to when clicked on enable the save:hover State its grabbing from my css.
The Problem
The button is inside the card aleady and everything in the css is setup for the parent div card.
my code structure simplified
<div className="card">
<-- CODE HERE -->
<Button className="myToggleButton" />
<div/>
I cant from what i can tell in css exclusively say myToggleButton:focus do these changes to the following classes since they are parent ones. So i think my only other way to do that is by telling it in react somehow to say that my div is in :hover state, but I can't quite figure out how despite my efforts. Thank you in advance for any help on this.

Instead of activating your animation with the :hover pseudo class, you can simply add or toggle another custom class, which would contain the values to transition or keyframes to animate, to the card element when the button is clicked. In this example I just use a transition, but you could also employ a more complex keyframe animation.
const card = document.querySelector(".card");
const button = document.querySelector(".card > button");
const animate = () => {
card.classList.toggle("small");
card.classList.toggle("big");
}
button.addEventListener("click", animate);
.card {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
color: white;
transition: all 250ms ease-in-out;
}
.small {
width: 200px;
height: 50px;
background-color: gray;
}
.big {
width: 400px;
height: 150px;
background-color: blue;
}
<div class="card small">
My Amazing Card
<button>Animate</button>
</div>

Related

Javscript detect the state of a div so I can toggle between 2 functions? (without jQuery)

I have a sidebar and I have 2 functions.
One opens it and the other one closes it.
Here are the two functions.
function openNav() {
document.getElementById("myNav").style.width = "100%";
}
function closeNav() {
document.getElementById("myNav").style.width = "0%";
}
I want to create a condition between both so I can toggle them but for that I need to detect if it's opened or closed.
How can I detect it so I can create a toggle function calling the functions above?
You don't need two functions. You can do this with just one CSS class and one function that toggles that one class on the sidebar element.
To do this, add the default width of 0% on the sidebar and then create another class, lets call it .sidebarToggle, that changes the width to 100%.
In javascript, you just need one function, lets call it toggleSidebar() that just toggles the .sidebarToggle class. This way, you not only need less code but also don't need to worry about checking whether the sidebar is opened or closed.
A Better Solution
A better approach is to change the transform property of the sidebar instead of the width. Changing transform property, in this case, is better as compared to changing width, because if you change the width, you will have to handle the:
resizing of the child elements of the sidebar as the sidebar's width is increased or decreased.
remove the left or right padding (if there's any) on the sidebar when the width of the sidebar is 0px or 0%. If you don't remove the padding, sidebar will not completely hide on 0px or 0% width.
With transform, you don't need to worry about the above mentioned points because, instead of resizing, we just translate the sidebar from one point to another.
Animating transform property is also more efficient as compared to animating the width property because changing transform property doesn't causes the browser to go through Layout and Paint steps of its critical rendering path whereas changing the width property will cause the browser to again go through these steps.
Following code snippet shows an example:
const btn = document.querySelector('button');
const sidebar = document.querySelector('.sidebar');
btn.addEventListener('click', () => {
sidebar.classList.toggle('sidebarToggle');
});
body {
margin: 0;
}
.sidebar {
display: flex;
flex-direction: column;
background: #666;
color: #fff;
padding: 15px 25px;
box-sizing: border-box;
width: 30%;
height: 100vh;
transform: translateX(-100%);
position: absolute;
transition: transform 0.5s ease;
}
span {
margin: 5px 0;
}
.sidebarToggle {
transform: translateX(0);
}
button {
position: absolute;
right: 0;
background: green;
color: #fff;
padding: 15px 25px;
}
<div class="sidebar">
<span>Item 1</span>
<span>Item 2</span>
<span>Item 3</span>
<span>Item 4</span>
</div>
<button>Toggle Sidebar</button>
This is best done with classes. That way, stying is better separated from behaviour and there is even a toggle function built in:
document.getElementById("button").onclick = function() {
document.getElementById("myNav").classList.toggle("width100");
};
#myNav {
background-color: red;
display: block;
height: 100px;
width: 0%;
}
#myNav.width100 {
width: 100%;
}
<nav id="myNav"></nav>
<button id="button">Toggle</button>

Start timer on mouseenter and clear on another div entry

I'm building a menu which, on hover of a unique-link will open another menu with links (mega menu).
The issue I'm having is that, on hover of said unique-link anchor, there's a gap between the anchor and the div shown on hover.
To explain, here's a demo:
$(function() {
var delay=3000, setTimeoutConst;
$(".unique-link a").on("mouseenter", function() {
setTimeoutConst = setTimeout(function() {
$('.showOnHover').addClass('show');
});
}).on("mouseleave", function() {
$('.showOnHover').removeClass('show');
});
});
.menu {
height: 100px;
background: lightgrey;
}
.menu .menu__options {
display: flex;
align-items: center;
height: 100%;
}
.menu .menu__options .item {
width: 100px;
display: flex;
align-items: center;
justify-content: center;
}
.menu .menu__options .item.a {
background: green;
}
.menu .menu__options .item.b {
background: red;
}
.showOnHover{
display: none;
height: 200px;
background:blue;
}
.show{
display: block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="menu">
<div class="menu__options">
<div class="item unique-link a">
Hover me
</div>
<div class="item b">
Link
</div>
</div>
</div>
<div class="showOnHover"></div>
In the demo you can see, when you hover over the "hover me" link and try to move your mouse onto the blue showOnHover div, the showOnHover disappears (which is correct, I only want the div to show on that anchor links over).
To work around this, my play was to start a timer once a user overs over the unique-link anchor. The way I'm planning this to work is the following:
User hovers over "hover me" link.
showOnHover div appears (at this point, the user is still hovering over the anchor)
If a user hovers over the "hover me" link and then moves their mouse onto the grey area (anywhere on menu__options), then start the timer a timer. The showOnHover div will be visible until this timer reaches 3 seconds.
If a user hovers over another a tag, then reset the timer (and don't show the showOnHover div).
Hopefully that all made sense. I've also demo'd my current approach in the snippet above. But cannot get the logic to work.
What i would suggest is to use a debounce method
lodash debounce or maybe Ben Alman jQuery throttle / debounce whichever you prefer.
So having a debounce method (i'll assume lodash), you can implement it like this:
var debouncedSetMenuVisible = _.debounce(function(show) {
if(show)
$('.showOnHover').addClass('show');
else
$('.showOnHover').removeClass('show');
}, 500);
$(".unique-link a, .showOnHover").on("mouseenter", function() {
debouncedSetMenuVisible(true);
}).on("mouseleave", function() {
debouncedSetMenuVisible(false);
});
This will ensure that when multiple events are happening, only the last one will be executed. Also it ensures that this will happen after the delay you specify.
You can use the debounced function only for mouseleave, but i would suggest you use it for mouseenter also as it is better UX (avoid showing mega menu for accidental hovers).
edit (one more thing): showOnHover needs also a mouseenter and a mouseleave event calling the same function (updated code).

On hover change all with same id with style 1 while changing the hovered div with style 2

I have some dynamic user generated divs.
I'm trying to create a function so when the user hovers on one of the divs it highlights while the other divs get blurred.
Therefore I need to figure out how (if possible) I can change the hovered div with one style while changing all the others with another style.
The generated divs are simply spawn through php as a simple div looking like this:
<div class="usercontainer" id="usercontainer"> </div>
I have tried something like this to change the div the user hovers on. But I can't figure out how I at the same time can change all the others.
Do I need javascript for it? or can it be done with css alone?
.usercontainer:hover
{
background-color: red;
opacity: 1.0;
}
I am sharing with css approach only, though you can do it by adding a class at parent with javascript.
Disadvantage of this approach is you have to use !important to override child styles.
.children {
display: inline-block;
height: 100px;
width: 100px;
background-color: grey;
color: red;
font-size: 50px;
border: solid 1px yellow;
}
.parent:hover .children {
opacity: 0.2;
}
.children:hover {
opacity: 1 !important;
}
<div class="parent">
<div class="children">1</div>
<div class="children">2</div>
<div class="children">3</div>
<div class="children">4</div>
</div>

Strange transition behavior for inline elements styles in certain places

This is a jsfiddle example file that replicates the problem: https://jsfiddle.net/Lhr0d6cw/11/
I wanted the element (when clicked) to expand for 6seconds from its original position but notice that when you click the red card (or any card), it doesn't start expanding from the originals position it used to be, but rather from the middle, I assume that its because transition of 6s to top and left is not being applied for some reason.
Only places I was able to make it work properly so far are stackoverflow editor below or by inserting a debugger in the code and doing it manually but when using my localhost or jsfiddle it doesn't transition properly.
This is the same example on stackoverflow which works as desired:
const productCards = document.querySelectorAll(".products__card");
productCards.forEach(c => {
// console.log("clicked1");
c.addEventListener("click", openCard)
});
function openCard(e) {
console.log("clicked");
console.dir(this);
let top = this.getBoundingClientRect().top;
let left = this.getBoundingClientRect().left;
// this.style.transition = "top 0.9s, left 0.9s";
this.style.top = top + "px";
this.style.left = left + "px";
this.style.position = "fixed";
console.log(`top: ${top}, left: ${left}`);
// debugger;
this.classList.add("open");
}
.products {
display: flex;
flex-wrap: wrap;
flex-direction: row;
justify-content: center;
min-width: 1000px;
max-width: 1500px;
margin-bottom: 300px;
}
.products .products__card {
display: flex;
flex-direction: column;
width: 150px;
height: 250px;
margin-bottom: 30px;
margin-right: 30px;
margin-left: 30px;
background-color: red;
transform: scale(1);
/* box-shadow: 3px 7px 55px -10px c(very-light); */
transition: width 0.9s, height 0.9s, z-index 0.9s, top 6s, left 6s;
}
.products .products__card.card-1 {
background-color: red;
}
.products .products__card.card-2 {
background-color: blue;
}
.products .products__card.card-3 {
background-color: green;
}
.products .products__card.card-4 {
background-color: yellow;
}
.products .products__card.card-5 {
background-color: pink;
}
.products .products__card.card-6 {
background-color: gray;
}
.products .products__card.open {
width: 550px;
height: 800px;
top: 50% !important;
left: 50% !important;
transform: translate(-50%, -50%) !important;
z-index: 120;
box-shadow: 0 0 1000px 1000px c(box-overlay);
}
<div class="products">
<div class="products__card card-1">
</div>
<div class="products__card card-2">
</div>
<div class="products__card card-3">
</div>
<div class="products__card card-4">
</div>
<div class="products__card card-5">
</div>
<div class="products__card card-6">
</div>
</div>
works when debugging:
The strange thing as mentioned above is that my problem in the browser using localhost is also solved when I insert debugger in the code and manually skip through the last step of adding .open class. If you have the same problem in jsfiddle or your own editor, try adding debugger; before this.classList.add("open"); and then open the console and then click the card and go over the last step manually in the console. you will notice that the card expanded from its original place as desired taking 6s to finish which means the transition was applied in this case.
My questions:
Why is transition for top and left only working in certain environments? is it a browser problem? I'm using the latest chrome. does someone know of a better way to achieve the same results?
code comments:
-obviously, 6 seconds is not what I will be using in my code, its used here just to make the transition obvious.
-In my source code, you can see that because I can't transition from position static to position fixed I had to use Javascript to add position fixed style inline to the element before the .open class is added, that way transition can take place properly when .open is added.
-I also added top and left values inline to keep the card in its original place when position: fixed style is applied because as you might know fixed position takes the element out of its flow, so top and left keep it in place.
-I added !important in css .open class because without it I can't override inline css as you might also know.
Thank you
I was able to solve my problem just now by applying a little hack. It seems that in some environments (localhost, jsfiddle) the javascript engine is adding the .open class faster than expected and the fact that it is working fine when debugging (slow process) indicated that to me. so I added a setTimeout() to the last piece of code delayed it by 20. this solved my problem and now it works fine on JSfiddle and on my computer. here is the new edited sample that works:
https://jsfiddle.net/Lhr0d6cw/14/
setTimeout(() => {
this.classList.add("open");
}, 20);
I would still like to know if there is a better way of doing this animation if someone would like to share!

on click function to reveal hover overlay - appears on click but won't disappear on 2nd click

I have a portfolio grid of images and when a user hovers or taps on a mobile a transparent overlay with some text and a button appears
I am using the on click function
It works fine on my touch screen laptop but not on my iOS phone or tablet
The overlay appears on first tap, but when I tap again it does not disappear unless I tap another grid image.
I would like it to disappear on 2nd tap
I have tried various ways of making this work, and the closest I have got it for it to disappear when another grid image is tapped
Here is my code:
HTML
<div class="col-xs-12 col-sm-7"><div class="image-wrap">
<div onclick="on()">
<img src="assets/images/pic.jpg">
<div class="overlay blue">
<h3>Portfolio item 1</h3>
<hr>
<p><strong>Coming Soon</strong><br> some overlay text here</p>
<br>
View Website
</div>
</div>
</div>
</div>
JS
function on() {
document.getElementById("overlay").style.display = "block";
}
function off() {
document.getElementById("overlay").style.display = "none";
}
CSS
.image-wrap {
display: inline-block;
position: relative;
}
.overlay {
position: absolute;
top:0;
left: 0;
width: 100%;
height: 100%;
color:white;
opacity: 0;
transition:opacity .5s ease-out;
text-align: center;
hr {
border: 1px solid #fff;
width: 10%;
}
}
.image-wrap:hover .overlay {
opacity: 1;
}
.red {
background: rgba(102,67,154,0.7);
}
.blue {
background: rgba(23,56,179,0.7);
}
.purple1 {
background: rgba(140,23,179,0.7);
}
.purple2 {
background: rgba(71,13,142,0.7);
}
}
I initially tried this with just CSS which gave me the desired result on all devices apart from iOS!
So I have decided to use the on click function to be more sure it works on all devices. I added the on click function to my existing code which I wrote to be used with CSS, but as I am rather new to JS I am wondering if I have it in the wrong place (the on-click)? I have tried lots of variations but this is the best I can get it to work
Any ideas of suggestions on how I can make the overlay disappear on the 2nd click would be great!
js fiddle
https://jsfiddle.net/49h450g9/14/
Please note: This works fine on touch-screen laptops, just not mobiles!
Thanks!
Your functions on and off on jsfiddle example are not working at all. What happening is your hover effect on normal screen which as the behavior of mobile work like focus on mobile device.
Moreover, from your description here I believe that you have more than one portfolio on your project. So you have several element with the id overlay and multiple use of same id is not validate for html and also will cause JavaScript error.
To let your project work properly follow my list below:
Make sure you have jQuery added on your project (generally before </body>)
Now let us thinks of these portfolio item below
<div class="portfolio">
<img src="images/portfolio-1.jpg" alt="...">
<div class="overlay">Link</div>
</div>
<div class="portfolio">
<img src="images/portfolio-2.jpg" alt="...">
<div class="overlay">Link</div>
</div>
<div class="portfolio">
<img src="images/portfolio-3.jpg" alt="...">
<div class="overlay">Link</div>
</div>
Then give the normal hover css styles inside media query like this. So that it never effect your js styles (I decide medias less than 992px as mobile device):
.portfolio{
background-color: #f1f1f1;
position: relative;
}
.portfolio .overlay{
background-color: rgba(0, 0, 0, 0.7);
display: block;
position: absolute;
top: 0;
left: 0;
opacity: 0;
transition: all 0.3s ease;
width: 100%;
height: 100%;
}
#media all and (min-width:992px){
.portfolio:hover .overlay{
opacity: 1;
}
}
Now with jQuery you can use event while user click any of the .portfolio item and toggle a class on it by which we will add further css to it:
$(document).ready(function(){
'use strict';
$(.portfolio).on('click', function(){
$(this).siblings('.portfolio').removeClass('hovered');
$(this).toggleClass('hovered');
});
});
Now it will add hovered class on 1st click and remove the hovered class on 2nd click. Also it will remove .hovered from other portfolio items. Now add the same css to it as the hover effect:
.portfolio.hovered .overlay{
opacity: 1;
}
Try this:
$("*").on("click, touchend", function(e) { $(this).focus(); });
or to achieve the opposite;
$("*").on("click touchend", function(e) { $(this).hover(); });
However the hover event doesn't work well on ios or other mobiles.
Another suggestion is to try replace any css using
:hover with :active.

Categories

Resources