React.js show part of component on hover issue - javascript

I have a problem with showing an overlay over my component. Below is the code.
<div className="content">
<div className="main" onMouseEnter={this.mouseOver} onMouseLeave={this.mouseLeaves}>
...
</div>
<div className="overlay" style={this.getOverlayStyle()} ref="overlay">
</div>
</div>
This is the style method:
getOverlayStyle: function() {
return {
display: 'none',
position: 'absolute',
top: '0',
bottom: '0',
left: '0',
right: '0',
zIndex: '100'
};
}
And these are the mouse event handlers:
mouseOver: function() {
this.refs.overlay.style.display = 'block';
},
mouseLeaves: function() {
this.refs.overlay.style.display = 'none';
},
Okay, so, when I hover over the content part, the overlay becomes visible, but it's flickering. When I stop moving the mouse above the content, the overlay is displaying nicely. When I move the mouse again, it flickers again, it's barely watchable, very annoying.
When I try making the overlay visible in the console (just your regular document.getElementsByClassName('overlay')[0].style.display = 'block', it works normally and doesn't flicker. I'm guessing this problem is due to my not yet great React knowledge.
Any ideas why is this happening?
Ps, I tried using a pure CSS solution: selector1 + selector2 { display: block; } and it behaved the same as above.
EDIT, I tried setting the visibility through state (change state on mouse enter and mouse leave), following a recommendation in the comments, but it still flickers when the mouse is moving.

I'm pretty sure you need to move the mouseenter and mouseleave to the .content div, not the .main div
what I expect is happening is this:
you enter main, the overlay is displayed which means the mouse leaves main and enters overlay. because it left main, the overlay is then hidden, and when the overlay is hidden, the mouse reenters main causing the cycle to rinse and repeat
although really this can be accomplished with css selectors. is there a reason you want to do this in react?

There is a pure css solution to this (codepen)
.content {
position: relative;
}
.main {
background-color: #0d0;
min-height: 30px;
min-width: 30px;
}
.overlay {
display: none;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
zIndex: 100;
background-color: #d00;
}
.content:hover .overlay {
display: block;
}

Related

preventDefault() on first touch but still allow scrolling/touchmove

I have a few elements on my site in which there is content that only appears when you hover on the element. A perfect example would be the Instagram feed grid. I'm pulling in the last 8 images from an Instagram account using their API and displaying them in a grid with flexbox. When you :hover over one of the images with a mouse, a semi-transparent black overlay fades in, containing the caption of the Instagram post.
On desktop you are able to click anywhere on the image/hovered caption overlay as they are both wrapped in an <a> element and doing so takes you to the post on Instagram's website. This works perfectly on desktop, but on touch devices does not.
Because you can't :hover on touch devices, I decided to implement some javascript on the touchstart event that uses e.preventDefault() to stop the link from being clicked when you tap the Instagram image and instead adds the CSS opacity: 1 and visibility: visible to the overlay element (which is what happens when you :hover on the element). Because the overlay element is then "on top" of the image when it's made visible, the link is clickable because the touchstart event is only listened for on the image.
This all works great and means that I can tap once on the image which fades in the overlay and doesn't take me to the image on Instagram, and I can then click for the second time on the overlay, taking me to the image on Instagram.
The problem
Because I am using e.preventDefault() in the touchstart event, if you touch the image and then start dragging your finger to scroll, the page doesn't scroll at all which results in a bad user experience.
So...
How can I only preventDefault() on a single touch event, or how can I prevent the link from being clicked on the first touch of the element but not on the second whilst still allowing the user to scroll/drag the page?
Slightly simplified version of my code
function toggleHoveredStateOn(e) {
e.preventDefault();
let hoverElem = this.querySelector('.js-touch-hover');
// remove all 'visible' classes from other "hovered" items first
document.querySelectorAll('.js-touch-trigger').forEach(touchTrigger => {
let hoverElem = touchTrigger.querySelector('.js-touch-hover');
Object.assign(hoverElem.style, {
visibility: 'hidden',
opacity: '0',
pointerEvents: 'none'
});
});
// add visible to the touched element's hover element
Object.assign(hoverElem.style, {
visibility: 'visible',
opacity: '1',
pointerEvents: 'all'
});
}
function initMobileTouchFunctionality() {
let touchTriggers = [...document.querySelectorAll('.js-touch-trigger')];
touchTriggers.forEach(touchTrigger => {
touchTrigger.addEventListener('touchstart', toggleHoveredStateOn, false);
touchTrigger.querySelector('.js-touch-hover').addEventListener('touchstart', function(e) {
e.stopPropagation();
}, false);
});
}
initMobileTouchFunctionality();
.flex-grid__item {
position: relative;
height: 263px;
border: 1px solid #FFF;
&:hover {
.flex-grid--social__hover {
opacity: 1;
visibility: visible;
pointer-events: auto;
}
}
img {
width: 100%;
height: 100%;
object-fit: cover;
}
}
.flex-grid--social__hover {
display: flex;
justify-content: center;
align-items: center;
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
width: 100%;
height: 100%;
text-align: center;
background-color: rgba(0, 0, 0, 0.8);
padding: 20px;
transition: opacity 0.2s ease-in-out;
opacity: 0;
visibility: hidden;
overflow: hidden;
pointer-events: none;
cursor: pointer;
}
<div class="flex-grid__item js-touch-trigger">
<a href="<?php // link to instagram. php code gets this ?>">
<img class="" src="<?php // instagram image. php code gets this ?>"/>
<div class="flex-grid--social__hover js-touch-hover">
<p><?php // instagram caption. php gets this ?></p>
</div>
</a>
</div>
You can decide whether to e.preventDefault or not using the e.target to see if the user tapped the image or the overlay.
You can read more at https://developer.mozilla.org/en-US/docs/Web/API/Event/target

How to show a dropdown when you hover over a specific area an on image? (HTML, CSS)

Is it possible to show a dropdown whenever you hover over some specific area on an image? For example, if my mouse is within 50,62 and 70,80. I already tried this with invisible boxes and divs, but the only way I could get them to overlay the image was with position properties, but they wouldn't stay in place if I reshaped or resized the screen. Any ideas?
Demo : http://jsfiddle.net/v8dp91jL/12/
The code is pretty self-explanatory.
Just two imp things:
Everything should be in %
the .dropdown is inside .hover-area so that when you move your mouse from .hover-area to .dropdown, .dropdown doesn't disappear because it is still technically inside .hover-area even tho it's visually not
You can add some hidden element (span) positioned on some specific area and it is going to trigger the hover:
HTML:
<div class="image-wrapper">
<span class="image-hover-trigger"></span>
<img src="..." >
<div class="dropdown"></div>
</div>
CSS:
.image-wrapper { position: relative; }
.image-hover-trigger { position: absolute; top: 20%; left: 20%; right: 20%; bottom: 20%; }
.dropdown { display: none; }
.image-hover-trigger:hover ~ .dropdown { display: block; }

How to implement momentum zooming

I would like to implement zooming like is done on this site (click the menu -> demo) or this site. As you can see you can zoom in and out with momentum.
So, my question was, how is that possible. My initial though was something like this
<main>
<section class="scrolling"></section>
<section class="overlay">
<button class="demo">Hello</button>
</section>
A .scrolling layer with an invisible scrollbar. The overlay positioned fixed on top of the .scolling layer with pointer-events: none
.scrolling {
height: 1000vh;
position: relative;
}
.overlay {
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
pointer-events: none;
}
and a bit of js
main.addEventListener('scroll', (e) => {
const scale = .... magic magic ....
overlay.style.transform = 'scale(...)';
});
DEMO
As you can see in the demo if you scroll the overlay has momentum-zoom.
But there is one huge problem with my demo
the button is not clickabe
the button doesn't show a mouse-pointer if you move over it
Any suggestions what I can do to fix the above issues ?
And are there libraries that support this feature? For example, I found Impetus.js but it doesn't seem to do momentum based on the scrolling gesture.

Slide trigger from top to bottom

I am using this for a slide trigger from top to bottom but on click my button is not moving from the top and the content is falling before button. I want as the content and my button to fall together.
Here you have my code for this.
HTML
<section class="drawer">
<header class="clickme">Click Me</header>
<div class="drawer-content">
CONTENT HERE
</div>
</section>
CSS
.drawer {
height: 800px;
overflow: hidden;
position: absolute;
width: 100%;
z-index: 5;
}
.drawer > header {
background: url(../img/UP-Discover.png);
background-repeat: no-repeat;
display: block;
height: 77px;
margin-left: 30%;
overflow: hidden;
width: 100%;
}
.drawer-content {
background: #fff;
padding-top: 22px;
background-repeat: no-repeat;
border-collapse: collapse;
height: 742px;
width: 100%;
border-bottom: 10px;
border-color: #000;
}
jQuery
$(function() {
$('.drawer').slideDrawer({
showDrawer: false,
// slideTimeout: true,
slideSpeed: 500,
slideTimeoutCount: 3000,
});
});
EDIT: This is the plugin that I am using. It was based on bottom. If I place it in bottom it will slide together.
(function(e) {
var t = {
init: function(n, r) {
if (n.showDrawer == true && n.slideTimeout == true) {
setTimeout(function() {
t.slide(r, n.drawerHiddenHeight, n.slideSpeed)
}, n.slideTimeoutCount)
} else if (n.showDrawer == "slide") {
t.slide(r, n.drawerHiddenHeight, n.slideSpeed)
} else if (n.showDrawer == false) {
t.hide(n, r)
}
e(".clickme").on("click", function() {
t.toggle(n, r)
})
},
toggle: function(n, r) {
e(r).height() + n.borderHeight === n.drawerHeight ? t.slide(r, n.drawerHiddenHeight, n.slideSpeed) : t.slide(r, n.drawerHeight - n.borderHeight, n.slideSpeed)
},
slide: function(t, n, r) {
e(t).animate({
height: n
}, r)
},
hide: function(t, n) {
e(n).css("height", t.drawerHiddenHeight)
}
};
e.fn.slideDrawer = function(n) {
var r = this.children(".drawer-content"),
i = parseInt(r.css("border-top-width"));
drawerHeight = this.height() + i;
drawerContentHeight = r.height() - i;
drawerHiddenHeight = drawerHeight - drawerContentHeight;
var s = {
showDrawer: "slide",
slideSpeed: 100,
slideTimeout: true,
slideTimeoutCount: 5e3,
drawerContentHeight: drawerContentHeight,
drawerHeight: drawerHeight,
drawerHiddenHeight: drawerHiddenHeight,
borderHeight: i
};
var n = e.extend(s, n);
return this.each(function() {
t.init(n, this)
})
}
})(jQuery);
You need to position your absolutely positioned drawer. Currently, it's just absolute. It's out of the flow of elements, but you need it "docked" to the bottom so that it's size, when it changes, starts bottom up.
I put together a fiddle with your code and some CSS liberties of my own. I also updated your header to have something in it to click. What #NewToJS was pointing out is that you had a header but nothing to click in it.
CSS for position:
.drawer {
bottom: 0;
max-height: 700px;
overflow: hidden;
position: absolute;
width: 100%;
z-index: 5;
}
Fiddle for you: http://jsfiddle.net/eaxkmh73/
Edited: I also forgot to mention you need to be careful with your heights. Part of the CSS liberties I took, aside from changing colors and playing with borders :), was to adjust the drawer and drawer-content heights. As they were, with an absolutely positioned drawer, when it grew the clickable header moved up past the top of the visible area. Obviously this means you can't click it closed. So, make sure you adjust your heights appropriately to keep items in view.
Also, in the future it is very helpful to put together one of these Fiddles yourself and provide the link with your question. That way, we can see exactly what you are experiencing and we can work off of that instead of putting together our own.
Update: After your latest comment I looked at what the script was doing and it looks fairly straight forward. Enough to do on your own with a simple DIY script. I am not sure if there is an advanced piece of functionality you need, but if not, the two Fiddles below offer optional approaches: One toggling a class for CSS transition; one for using jQuery slideToggle.
CSS Transition: http://jsfiddle.net/ceh0t3vv/3/
slideToggle: http://jsfiddle.net/11w2ufdr/2/
I left the option of supplying a 'data-target' attribute to the header if you wanted more flexibility in what you used for the content. But they can be simplified even further.
CSS simplified: http://jsfiddle.net/ceh0t3vv/4/
slideToggle simplified: http://jsfiddle.net/11w2ufdr/3/
Obviously the CSS is adjusted for each of those as well.
Updated Per latest comment, here is an example with positioning and margin applied to elements: http://jsfiddle.net/ceh0t3vv/5/
You could also combine a toggleClass and slideToggle, though you would need way more finesse than I put into this example. This is more of an example of why it might be better to position and z-index the actual drawer from the start, rather than toggle on open/close. Honestly, just less effort:
http://jsfiddle.net/11w2ufdr/4/

grow/shrink image gallery with jquery WITHOUT affecting the layout

I got an image gallery organized as an <ul>. all images are in <li> elements and when I move my mouse over one of those pictures, it should grow to give the user a visual feedback. thing is, when I just change the size of the image using animate(), the other pictures will be pushed to the side as the resized image uses more space.
therefore I went with cloning the image element, float it right over the original image and then calling animate. this comes with the problem that onMouseOut() is called as soon as the cloned images pops up. so I need a nested hover() function and this is where things got complicated.
I got two errors and I can't find out whats causing them. the first one is, that animate() won't let the cloned image grow beyond the right border of its original, the second is, that I get weird grow/shrink behavior, when moving my mouse quickly over the gallery.
html:
<ul id="gallery1" class="gallery_container">
<li class="frame">
<img src="pic1.jpg" class="picture" /></li><li class="frame">
<img src="pic2.jpg" class="picture" /></li><li class="frame">
<img src="pic3.jpg" class="picture" /></li>
</ul>
css:
.picture
{
height: 200px;
border: 0px;
margin: 0px;
padding: 0px;
}
.frame
{
display: inline-block;
position: relative;
margin: 0px;
margin-right:8px;
padding: 0px;
}
.frame a
{
padding: 0px;
margin: 0px;
}
.gallery_container
{
height: 200px;
width: 150%;
position: relative;
top: 4px;
padding: 0px;
margin: 0px;
}
and finally the code that is giving me those headaches:
$(document).ready(function()
{
var zooming = false;
var zoom = 4;
var speed_zoom = 100;
$('.gallery_container li a').hover(function(element)
{
// disable zooming to prevent unwanted behavior
if(zooming) return;
zooming = true;
$(this).after( $(this).clone(false) );
$(this).next().attr('id', 'focus_frame');
},
function(element) // when the new element pops up, onmouseout is triggered, since the focus_frame is in front of the image
{
$(this).next().hover(function(element)
{
// we need to re-position the element in the dom-tree, since it needs to grow out of a container with overflow: hidden
$('#focus_frame img').animate({'left' : zoom * -1, 'top' : zoom * -1, 'height' : 200+(zoom*2), 'width' : $('#focus_frame img').outerWidth() + (zoom*2)}, speed_zoom);
},
function(element)
{
$(this).remove();
zooming = false;
});
});
});
var $doc=$(document.body)
$doc.on({
"mouseenter" : function (e) {
$doc.find("> .gallery_clone").remove();
var $i=$(this).parent();
$i.pos = $i.offset();
$i.clone()
.addClass("gallery_clone "+$i.parent().parent().attr("class"))
.css({
top:(Math.round($i.pos.top)-3)+"px"
,left:(Math.round($i.pos.left)-3)+"px"
,width:$i.width()
}).appendTo($doc);
}
},
" ul > li > img"
).on ({
"mouseleave" : function (e) {
$(this).remove();
},"> .gallery_clone");
in css .gallery_clone is position:absolute
then i animate .gallery_clone:hover through css but you can do it in the jquery as well i guess, adding a mouseenter event on .gallery_clone
edit : i've literally copy/pasted from my script so you'll have to adapt this code to your html
nb: give css anim a go, it's worth it even if older ie will not animate; (i also made lightbox effect almost pure css for that same gallery - will publish later, not ready for plugin release just now sorry)
nb2: that part "+$i.parent().parent().attr("class") is because in the cms they can chose gallery background color so adding that class forward the background color & other gallery style to the clone (ie you should not need it)

Categories

Resources