What I'm trying to achieve is:
On desktop: 1) Hover to bring up overlay 2) Click to activate
On touch: 1) Tap to bring up overlay 2) Tap again to activate
What I've come up with so far is the below. However when using a touch device it fires the both touchend event and then the click event, this causes the overlay click event to be fired unnecessarily. What's the best way around this, if any?
$(".container > .item").on("mouseenter mouseleave", function(e) {
$(this).toggleClass("hover");
console.log("hover: " + e.type);
});
$(".container > .item > .overlay").on("mouseup touchend", function(e) {
console.log("click: " + e.type);
})
.item {
width: 200px;
height: 200px;
background: red;
display: inline-block;
position: relative;
}
.item.hover > .overlay {
display: block;
}
.overlay {
display: none;
position: aboslute;
top: 0;
left: 0;
height: 100%;
width: 100%;
background: green;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="container">
<div class="item">
<div class="overlay">
</div>
</div>
</div>
I came up with the below, which works rather nicely -- And when changing between touch/pen and mouse.
In effect, for mouse navigation, mouseover 'wakes up' the element, and readies the listeners, then click gets handled by the listeners. For touch navigation, the first tap 'wakes up' the element, and the second tap gets handled by the listeners.
var hoverCapture = (function() {
var TOUCH_STATE = {
'initial': 0,
'over': 1,
'click': 2
};
var eventTargetDefault = '.hover-layer';
function hoverCapture(selectorScope, eventTarget) {
var eventTarget = typeof eventTarget !== 'undefined' ? eventTarget : eventTargetDefault;
var $eventTarget = $(selectorScope).find(eventTarget);
var touchState = TOUCH_STATE.initial;
var previousEventType = '';
$eventTarget.on('mouseenter', function(e) {
$(this).addClass("hover");
previousEventType = e.type;
});
$eventTarget.on('mouseleave', function(e) {
$(this).removeClass("hover");
// Order of events is:
//
// outside -> .image
// touchend, mousenter
//
// .image -> .image
// touchend, mouseleave, mouseenter
//
// .image -> outside
// mouseleave
//
// When tapping out, we don't receive a touchend
// event, since the touchend happens outside of
// our scoped listeners
if (previousEventType === 'touchend') {
touchState = TOUCH_STATE.over;
} else {
touchState = TOUCH_STATE.initial;
}
previousEventType = e.type;
});
$eventTarget.on('touchend', function(e) {
if (touchState === TOUCH_STATE.initial) {
touchState = TOUCH_STATE.over;
} else {
touchState = TOUCH_STATE.click;
}
previousEventType = e.type;
});
$eventTarget.each(function() {
this.addEventListener("click", function(e){
if (touchState === TOUCH_STATE.over) {
e.stopPropagation();
}
previousEventType = e.type;
}, true);
});
}
return hoverCapture;
}());
hoverCapture("#gallery1", ".image");
hoverCapture("#gallery2", ".image");
$(".test").on("click", function(e) {
alert("Ouch!");
});
.gallery {
margin: 5px;
}
.image {
width: 100px;
height: 100px;
background: grey;
margin: 0 -4px -4px 0;
display: inline-block;
}
.image .overlay {
height: 100%;
width: 100%;
background: red;
display: none;
position: relative;
}
.image.hover .overlay {
display: block;
}
.test {
width: 25%;
height: 25%;
position: absolute;
right: 0;
background: purple;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="gallery" id="gallery1">
<div class="image">
<div class="overlay">
<div class="test"></div>
</div>
</div>
<div class="image">
<div class="overlay">
</div>
</div>
<div class="image">
<div class="overlay">
</div>
</div>
<div class="image">
<div class="overlay">
</div>
</div>
</div>
<div class="gallery" id="gallery2">
<div class="image">
<div class="overlay">
<div class="test">
</div>
</div>
</div>
<div class="image">
<div class="overlay">
</div>
</div>
<div class="image">
<div class="overlay">
<div class="test">
</div>
</div>
</div>
<div class="image">
<div class="overlay">
</div>
</div>
</div>
If anyone has any additions/suggestions please do follow up.. This has been giving me headaches for days now!
Related
I have a list of dom element:
When I click on the element, it will toggle open and close.
When I click on the child of the open one, it will not close the parent.
When I click on another one, it will open the one which is clicked and close the others.
When I click outside the element that has been opened, it will close then open.
-> The problem is I can't archive the first one. Does somebody know what I'm doing wrong here?
$(".front").click(function() {
$(".front").not(this).removeClass("active");
$(this).addClass("active");
});
$(document).mouseup(function(n) {
var t = [];
t.push($(".front"));
$.each(t, function(t, i) {
$(i).is(n.target) || $(i).has(n.target).length !== 0 || $(i).removeClass("active")
})
});
.front {
position: relative;
background-color: red;
width: 100px;
height: 100px;
margin: 20px;
}
.front .back {
position: absolute;
top: 50%;
left: 150px;
display: none;
background-color: green;
width: 50px;
height: 50px;
}
.front.active .back {
display: block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="front">
<div class="back"></div>
</div>
<div class="front">
<div class="back"></div>
</div>
<div class="front">
<div class="back"></div>
</div>
just add toggle class after your first line:
$(".front").click(function() {
$(".front").not(this).removeClass("active");
$(this).toggleClass("active");
});
Is it possible to move .item from .region1 to .region2 when the anchor tag is clicked, and also move it back to .region1 when it is clicked again? So pretty much the behavior of a toggle ?
<a href="#" class="btn">Click</div>
<div class="region1">
<div class="item"></div>
</div>
<div class="region2"></div>
make use of append() to achieve the toggle effect:
function toggleItem(){
var $item = $('.region1').find('.item');
if($item.length !== 0){
$('.region2').append($item);
} else {
$item = $('.region2').find('.item');
$('.region1').append($item);
}
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<a href="#" class="btn" onclick='toggleItem()'>Click</a>
<div class="region1">Inside region 1
<div class="item">item</div>
</div>
<div class="region2">Inside region 2</div>
Just build the toggle like function by yourself. Check if the element is is there then use appendTo to move the element.
$('a.btn').click(function() {
if ($('.region1 > .item').length) {
$('.region1 > .item').appendTo('.region2');
}
else {
$('.region2 > .item').appendTo('.region1');
}
});
.region1 { width: 50px; height: 50px; background: red; }
.region2 { width: 50px; height: 50px; background: blue; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Click
<div class="region1">
<div class="item">item</div>
</div>
<div class="region2"></div>
If there is only one .item in the whole page, you could make it shorter:
$('a.btn').click(function() {
$('.item').appendTo($('.region1 > .item').length ? '.region2' : '.region1');
});
.region1 { width: 50px; height: 50px; background: red; }
.region2 { width: 50px; height: 50px; background: blue; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Click
<div class="region1">
<div class="item">item</div>
</div>
<div class="region2"></div>
Or without jQuery:
document.querySelector('a.btn').addEventListener('click', function() {
if (item = document.querySelector('.region1 > .item')) {
document.querySelector('.region2').append(item);
} else if (item = document.querySelector('.region2 > .item')) {
document.querySelector('.region1').append(item);
}
});
.region1 { width: 50px; height: 50px; background: red; }
.region2 { width: 50px; height: 50px; background: blue; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Click
<div class="region1">
<div class="item">item</div>
</div>
<div class="region2"></div>
Here's a solution in vanilla JavaScript:
var item = document.querySelector('.item');
var region1 = document.querySelector('.region1');
var region2 = document.querySelector('.region2');
document.querySelector('a').addEventListener('click', function() {
if (region1.contains(item)) {
region2.appendChild(item);
} else if (region2.contains(item)) {
region1.appendChild(item);
}
});
.region1 {
width: 100px;
height: 100px;
background-color: aqua;
}
.region2 {
width: 100px;
height: 100px;
background-color: yellow;
}
Click
<div class="region1">
<div class="item">Test</div>
</div>
<div class="region2"></div>
Note:
Because there's probably more than just one single a tag in your page, you should not use ...querySelector('a')... like this. To ensure the proper a tag gets selected, you should make it unique, e. g. by adding an id to it. If you decide to use the way with the id, replace the your querySelector('a') with querySelector('#yourid').
And thank you to eisbehr for helping me to cut down the script in an very effective way.
I just want the ability to be able to pause the animation on mouse hover. I trying to looking good way to do that, but there was some issues. I tested some hover/stop functions, but I can't get these working correctly.
jQuery(document).ready(function() {
setInterval(function() {
jQuery('#testimonials .slide').filter(':visible').fadeOut(1000, function() {
if (jQuery(this).next('.slide').size()) {
jQuery(this).next().fadeIn(1000);
} else {
jQuery('#testimonials .slide').eq(0).fadeIn(1000);
}
});
}, 5000);
});
#quote {
width: 100%;
height: 130px;
background-position: center bottom;
background-repeat: no-repeat;
margin-bottom: 65px;
overflow: hidden;
}
#testimonials .slide {
color: #555555;
}
#testimonials .testimonial-quote {
display: inline;
width: 600px;
height: 170px;
margin: 0;
padding: 0;
float: left;
position: relative;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="quote">
<div id="testimonials">
<div class="slide">
<div class="testimonial-quote">
<p>Text 1</p>
<h4 class="title">Title 1</h4>
</div>
</div>
</div>
</div>
You can achieve this by calling clearInterval() when the slide is hovered, then re-creating the interval again when the mouse leaves the slide, something like this:
jQuery(document).ready(function($) {
var $slides = $('#testimonials .slide');
function beginSlideInterval() {
return setInterval(function() {
$slides.filter(':visible').fadeOut(1000, function() {
var $next = $(this).next().length ? $(this).next() : $slides.first();
$next.fadeIn(1000);
});
}, 3000);
}
var slideInterval = beginSlideInterval();
$slides.on('mouseenter', function() {
clearInterval(slideInterval);
}).on('mouseleave', function() {
slideInterval = beginSlideInterval();
});
});
#quote {
width: 100%;
height: 130px;
background-position: center bottom;
background-repeat: no-repeat;
margin-bottom: 65px;
overflow: hidden;
}
#testimonials .slide {
color: #555555;
}
#testimonials .testimonial-quote {
display: inline;
width: 600px;
height: 170px;
margin: 0;
padding: 0;
float: left;
position: relative;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="quote">
<div id="testimonials">
<div class="slide">
<div class="testimonial-quote">
<p>Text 1</p>
<h4 class="title">Title 1</h4>
</div>
</div>
</div>
</div>
Note that I shortened the interval to make the effect more obvious.
The code below works in Firefox, but having problem when executing in Chrome.
The idea is simple; after click change the 'max-height' property and show the rest of the text. Height is computed for each element itself and transition is executed via jQuery's .transition(). JS does it's work, but transition is represented poorly. It looks little better if I reduce the transition time, but still very far from expected.
Any ideas on how to fix representation in Chrome?
$(function() {
var Readmore = function(el) {
this.el = el || {};
var descriptionElement = this.el.find('#predmetDesc');
var chevronElement = this.el.find('#showMore');
var links = descriptionElement.add(chevronElement);
links.on('click', this.sloppy);
};
Readmore.prototype.sloppy = function() {
$this = $(this).parents().eq(1).find('#paragraph');
$sibling = $this.siblings('#showMore');
var expanded = $this.is('.expanded');
if (expanded) {
$this.transition({ 'max-height': '96px',
overflow: 'hidden'
}, 500, 'in', function() {
$this.removeClass("expanded");
});
$sibling.transition({
'rotate': '0deg'
}, 500, 'in', function() {
$sibling.removeClass("expanded");
});
} else {
$this.transition({
'max-height': $this.get(0).scrollHeight,
overflow: ''
}, 500, 'out', function() {
$this.addClass("expanded");
});
$sibling.transition({
'rotate': '180deg'
}, 500, 'out', function() {
$sibling.addClass("expanded");
});
}
};
var readMore = new Readmore($('.sviPredmetiCol'), false);
});
.paragraph {
display: inline-block;
max-height: 96px;
overflow: hidden;
padding-top: 23px;
margin-bottom: -4px;
}
div.paragraph > p {
text-decoration: none !important;
cursor: pointer;
}
p {
margin: 0;
}
#predmetDesc {
position: relative;
/*max-height: 97px; !*72px*!*/
}
#showMore {
position: relative;
display: block;
text-align: center;
line-height: 1;
cursor: pointer;
margin-bottom: -8px;
}
.fa-chevron-down::before {
position: relative;
cursor: pointer;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="row sviPredmetiRow">
<div id="sviPredmetiId" class="col-xs-12 col-md-12 sviPredmetiCol">
<div id="parentAbs" class="panel panel-primary sviPredmetiPanel">
<div class="panel-heading">
<div class="panelMainTitle">courseName</div>
</div>
<div class="panel-body">
<div class="widget">
<div class="statEcts">
<div class="ects">
<span class="count">courseInfo</span>
<span class="desc">ECTS</span>
</div>
</div>
<div class="statCourse">
<div class="courseId">
<span class="count">courseID</span>
<span class="desc">ID predmeta</span>
</div>
</div>
</div>
<div id="paragraph" class="paragraph">
<p id="predmetDesc">Course description</p>
</div>
<!--/div .paragraph-->
<div id="showMore"><i class="fa fa-chevron-down"></i>
</div>
</div>
<!--/div .panel-body-->
</div>
<!--/div .sviPredmetiPanel-->
</div>
<!--/div .sviPredmetiCol-->
</div>
<!--/div sviPredmetiRow-->
<div id="home">
<div id="logo"> </div>
<div id="foot"> <div class="button"> CLICK ME</div>
<div class="button two"> CLICK ME</div> </div>
</div>
<div id="show">
TEST TEST TEST TEST
</div>
$('.button').click(function(){
$('#show').show();
})
#home {
width: 400px;
height: 400px;
background-color: #ccffff;
}
#logo {
width: 100%;
height: 300px;
background-color: #0099ff;
}
#foot {
width: 100%;
height: 100px;
background-color: #009999;
}
.button {
width: 90px;
height: 30px;
background-color: red;
margin-left: 50px;
}
i would like if i click RED .button then GREEN .show show me over the red button and if i click outside GREEN .show then this hide.
LIVE: http://jsfiddle.net/YeE4p/1/
Is this somewhat close to what you are looking for?
http://jsfiddle.net/neilheinrich/YeE4p/6/
I had to change the #show div to be absolutely positioned and use this javascript:
$('.button').click(function(){
var $show = $('#show');
var position = $(this).offset()
$show.css({
"left": position.left + "px",
"top":position.top + "px"
}).show();
$(window).bind("mousedown", function(e){
if (!($(e.target).attr("id") === "show")) {
$("#show").hide();
$(window).unbind("mousedown");
}
});
})
Try this out
http://jsfiddle.net/Quincy/YeE4p/4/
$('.button').click(function(event){
$('#show').show();
event.stopPropagation();
})
$('body').click(
function(){
$('#show').hide();
}
)