jQuery Mobile: Swipeleft/Swiperight is jumping itself - javascript

i use this code to react on the swipeleft/swiperight event:
$('body').live('pagecreate', function(event) {
$('div[data-role="page"]').live("swipeleft", function() {
var nextpage = $(this).next('div[data-role="page"]');
// swipe using id of next page if exists
if (nextpage.length > 0) {
$.mobile.changePage(nextpage);
}
});
$('div[data-role="page"]').live("swiperight", function() {
var prevpage = $(this).prev('div[data-role="page"]');
// swipe using id of previous page if exists
if (prevpage.length > 0) {
$.mobile.changePage(prevpage, {
reverse : true,
});
}
});
});
It works, but after about 3 swipes (maybe when i reach the end of the 4 pages) there´s no normal behaviour anymore. For example: I swipe left --> i get the nextpage but then it swipes back and then again (i reach the expected page but not in that case i want). That happens after about 3 swipes all the time. What´s wrong with the code?
Thx a lot!

You know there is a plugin from the JQM devs just for that: JQM pagination
I think your problem is related to multiple bindings.
Put a console.log in every binding to see how often it fires. Like so:
$('body').live('pagecreate', function(event) {
console.log( "PAGECREATE fired")
$('div[data-role="page"]').live("swipeleft", function() {
console.log("binding to swipe-left on "+$(this).attr('id') );
var nextpage = $(this).next('div[data-role="page"]');
// swipe using id of next page if exists
if (nextpage.length > 0) {
$.mobile.changePage(nextpage);
}
});
$('div[data-role="page"]').live("swiperight", function() {
console.log("binding to swipe-right "+$(this).attr('id');
var prevpage = $(this).prev('div[data-role="page"]');
// swipe using id of previous page if exists
if (prevpage.length > 0) {
$.mobile.changePage(prevpage, {
reverse : true,
});
}
});
});
If these fire more than once, you will attach multiple bindings to your pages, which will all trigger changePage on swipe, when you only want one event to fire with every swipe.
EDIT:
First up, if you are using latest Jquery you should bind using on/off and not use live anymore.
One way would be to unbind on pagehide and re-bind when the page is reloaded. I guess that would be recommended way. However if you are not removing the page from the DOM when swiping to the next page, you will unbind and since pagecreate will not fire again (page still in the DOM, no need to create), you will not bind again when you swipe back.
I'm also dealing with this a lot and am using this:
$(document).on('pageshow', 'div:jqmData(role="page")', function(){
var page = $(this), nextpage, prevpage;
// check if the page being shown already has a binding
if ( page.jqmData('bound') != true ){
// if not, set blocker
page.jqmData('bound', true)
// bind
.on('swipeleft.paginate', function() {
console.log("binding to swipe-left on "+page.attr('id') );
nextpage = page.next('div[data-role="page"]');
if (nextpage.length > 0) {
$.mobile.changePage(nextpage);
}
})
.on('swiperight.paginate', function(){
console.log("binding to swipe-right "+page.attr('id');
prevpage = page.prev('div[data-role="page"]');
if (prevpage.length > 0) {
$.mobile.changePage(prevpage, {
reverse : true,
});
};
});
}
});
This will fire with every pageshow and check if the page is bound. If not, it sets the bindings on this page. The next time pageshow fires bound will be true, so it will not re-bind. If the page is removed from the DOM and reloaded, bound will not be set and the binding will be reset.
I have also added .paginate to your swipeleft/swiperight so you could remove them all at once using off

Related

How to prevent slide change (navigating to another slide) in vue-slick-carousel?

I'm using vue-slick-carousel to load schedule data for given slide (which is a month in my case).
<vue-slick-carousel
#afterChange="afterChange"
#beforeChange="beforeChange"
#swipe="swipe"
class="text-center"
:dots="true"
:arrows="true"
:initialSlide="getCurrentScheduleIndex"
ref="mycarousel"
v-if="this.schedules.length && !timeline_view"
>
<template v-for="schedule in schedules">
<div :key="schedule.id" class="schedule-slick-name">
<em>{{ schedule.name }}</em>
</div>
</template>
</vue-slick-carousel>
If user make some changes in the schedule and accidentially clicks to change the slide I would like to warn user about unsaved changes and cancel carousel navigation. With native slick.js I was just invokig event.preventDefault() like below:
$('.mySlick').on('beforeChange', function(event, slick, currentSlide, nextSlide){
if (unsaved){
event.preventDefault();
...
But I have no idea how to do it in in vue. There is beforeChange event emitted as described in documentation but as I understand there is only currentSlide and nextSlide emitted.
beforeChange(currentSlide, nextSlide) {
if (this.unsavedChanges) {
if (
this.confirmLeave(
"Are you sure you want to leave? Unsaved changes will be lost."
)
) {
// Prevent from navigating to another slide like event.preventDefault();
}
} else {
// Proceed with slide change ...
}
},
I was trying to use also another similar library vue-slick where there is an original jQuery event emitted but calling event.preventDefault() just not work:
beforeChange(event, slick, currentSlide, nextSlide) {
if (this.unsavedChanges) {
if (
this.confirmLeave(
"Are you sure you want to leave? Unsaved changes will be lost."
)
) {
// Prevent from navigatiing to another slide like event.preventDefault();
event.preventDefault() // this does not work
}
} else {
// Proceed with slide change ...
}
},
Maybe someone had the same problem? Thank you for any answer.
I decided to modify vue-slick-carousel library by adding new prop (allowToSlideChange) which just simulate "cancel navigatiation" behavior:
If user does not cofirm that want's to change schedule month (change carousel slide) the cancelNavigation event is emitted instead of afterChange (where I invoke schedule load for selected month). I know that this is one of those ugly solutions but it works in my case pretty good :)
slideHandler(index, dontAnimate = false) {
let navigate = true;
const { asNavFor, speed } = this;
// capture currentslide before state is updated
const currentSlide = this.currentSlide;
if (!this.allowToSlideChange) {
navigate = window.confirm(this.slideChangeWarningMsg);
if (!navigate) {
index = currentSlide; // If user cancel navigation just set the same slide as before
}
}
...
navigate
? this.$parent.$emit("afterChange", state.currentSlide)
: this.$parent.$emit("navigationCanceled", state.currentSlide);

Trigger jQuery mousemove event only once

I'm trying to make an exit popup and I could do that using the following code.
Whenever the user's mouse move out of the browser area, this gives a popup. But it is quite annoying when the popup comes everytime. I want to limit it to just a single time.
Can somebody help me with this?
jQuery(document).ready(function () {
jQuery(document).mousemove(function(e) {
jQuery('#exitpopup').css('left', (window.innerWidth/2 - jQuery('#exitpopup').width()/2));
jQuery('#exitpopup').css('top', (window.innerHeight/2 - jQuery('#exitpopup').height()/2));
if(e.pageY <= 5)
{
// Show the exit popup
jQuery('#exitpopup_bg').fadeIn();
jQuery('#exitpopup').fadeIn();
}
});
});
Use jQuery's one() function: http://api.jquery.com/one/
jQuery(document).ready(function () {
jQuery(document).one('mousemove', function(e) {
jQuery('#exitpopup').css('left', (window.innerWidth/2 - jQuery('#exitpopup').width()/2));
jQuery('#exitpopup').css('top', (window.innerHeight/2 - jQuery('#exitpopup').height()/2));
if(e.pageY <= 5)
{
// Show the exit popup
jQuery('#exitpopup_bg').fadeIn();
jQuery('#exitpopup').fadeIn();
}
});
});
Insert this:
e.stopPropagation();
just at the first list of the mousemouve function.
....
jQuery(document).mousemove(function(e) {
e.stopPropagation();
jQuery('#exitpopup').css('left', (window.innerWidth/2 - jQuery('#exitpopup').width()/2));
...
(function($) {
$(document).ready(function () {
var leftPage = false;
$(document).mousemove(function(e) {
if(e.pageY <= 5)
{
if (!leftPage) {
var exitPopup = $('#exitpopup');
exitPopup.css('left', (window.innerWidth/2 - exitPopup.width()/2));
exitPopup.css('top', (window.innerHeight/2 - exitPopup.height()/2));
$('#exitpopup_bg').fadeIn();
exitPopup.fadeIn();
}
leftPage = true;
} else {
leftPage = false;
}
});
});
})(jQuery);
"If the user leaves the page AND they have not already left THEN set popup. Next mark that they have left the page (leftPage = true)"
"Do not try and set the popup again until they are back in the page"
Couple of extras:
Instead of calling jQuery all the time we wrap the whole thing in a function wrapper so you can use $.
Instead of doing this everytime $('#exitpopup'); we CACHE it to a variable exitPopup so it doesn't have to do the lookup every time (inefficient)
A few things here. First, for form's sake, you should move your CSS alterations inside the if block, because you really don't need those to run every time the user moves their mouse, just right before you show the popup:
if(e.pageY <= 5)
{
// Alter CSS as appropriate
jQuery('#exitpopup').css('left', (window.innerWidth/2 - jQuery('#exitpopup').width()/2));
jQuery('#exitpopup').css('top', (window.innerHeight/2 - jQuery('#exitpopup').height()/2));
// Show the exit popup
jQuery('#exitpopup_bg').fadeIn();
jQuery('#exitpopup').fadeIn();
}
Second, you'll probably want to avoid showing it a second time by detaching the event handler. I'd recommend you use the jQuery .on() and .off() syntax instead of the shorthand .mousemove() because it'll be easier to read and maintain. I also recommend you use namespaces on your events so you can ensure that you're not detaching events that might have been set in other scripts.
jQuery(document).on('mousemove.yourNamespace', function (e) {
if(e.pageY <= 5)
{
// Alter CSS as appropriate
jQuery('#exitpopup').css('left', (window.innerWidth/2 - jQuery('#exitpopup').width()/2));
jQuery('#exitpopup').css('top', (window.innerHeight/2 - jQuery('#exitpopup').height()/2));
// Show the exit popup
jQuery('#exitpopup_bg').fadeIn();
jQuery('#exitpopup').fadeIn();
// now detach the event handler so it won't fire again
jQuery(document).off('mousemove.yourNamespace');
}
}
Lastly, if you wrap all of this code in an IIFE, you won't have to write out jQuery every time, and you still won't have to worry about possible conflicts with $ in the global namespace.
(function ($) {
$(document).on('mousemove.yourNamespace', function (e) {
if(e.pageY <= 5)
{
// Alter CSS as appropriate
$('#exitpopup').css('left', (window.innerWidth/2 - $('#exitpopup').width()/2));
$('#exitpopup').css('top', (window.innerHeight/2 - $('#exitpopup').height()/2));
// Show the exit popup
$('#exitpopup_bg').fadeIn();
$('#exitpopup').fadeIn();
// now detach the event handler so it won't fire again
$(document).off('mousemove.yourNamespace');
}
}
})(jQuery);
jQuery docs for .on(), .off(), and event.namespace for reference.

Issues with event.preventDefault() not working

looking for some advice around the correct usage of preventDefault() - code is below. In short, I don't want the a.dropdownTrigger click event to jump to the anchor. I thought inserting event.preventDefault() as the first line of that function would do the trick, but apparently not.
Thinking it might have something to do with having bound the hashchange event, which I'm using to monitor changes to the url (clicking the a.dropdownTrigger element updates the hash location, which calls the listener on hashchange - doing it this way enables me to catch inbound links to a dropdownTrigger, and maintain url history).
Any ideas where I'm going wrong?
// check inbound anchor links against dropdown ids
if (hash != "" && dropdowns.length != 0 && $.inArray(hash, dropdowns)) {
OpenDropdownForHash(hash);
}
// listen for hashchange once page loads (handles on-page links to dropdown content)
$(window).bind('hashchange', function () {
hash = window.location.hash;
if (dropdowns.length != 0 && $.inArray(hash, dropdowns)) {
OpenDropdownForHash(hash);
}
});
// open the targeted dropdown - var incoming is a bool, differentiate between inbound links and on-page clicks
function OpenDropdownForHash(x) {
$(x).next('.dropdown').toggleClass('open').slideToggle(200);
if ($(x).next('.dropdown').hasClass('open')) { //can this live in the callback above?
$(x).parent().css("-webkit-transition", "all 0.8s ease")
.css("backgroundColor", "white")
.css("-moz-transition", "all 0.8s ease")
.css("-o-transition", "all 0.8s ease")
.css("-ms-transition", "all 0.8s ease")
.css("backgroundColor", '#eeeeee').delay(600).queue(function () {
$(this).css("backgroundColor", "white");
$(this).dequeue(); //Prevents holding color with no fadeOut on second click.
});
}
}
// finally, the basic click handler for dropdowns - update the hash (to allow history), which triggers previously bound hashchange event
$('a.dropdownTrigger').bind('click',function (e) {
e.preventDefault();
if ("#" + $(this).attr('id') == location.hash) { // clicking an open dropdown link doesn't trigger the hashchange event, so we check manually
OpenDropdownForHash("#" + $(this).attr('id'));
}
else { // clicking a closed dropdown does call hashchange, so the OpenDropdownForHash function is called by the listener
location.hash = $(this).attr('id');
}
});
UPDATE: Solved this with a bit more effort, reworked the click handler and simplified the hashchange listener:
if (dropdowns.length != 0) {
// the basic click handler for dropdowns - update the hash (to allow history), which triggers previously bound hashchange event
$('#mainContentContainer a').bind('click', function (e) {
var target = $(this).attr('href') != null ? $(this).attr('href') : "#" + $(this).attr('id');
var offset = window.pageYOffset;
if ($.inArray(target, dropdowns) && location.hash != target) {
location.hash = target;
window.scrollTo(0, offset);
}
else if ($.inArray(target, dropdowns) && location.hash == target) {
OpenDropdownForHash(location.hash, $(this).hasClass('dropdownTrigger'));
window.scrollTo(0, offset);
}
});
}
$(window).bind('hashchange', function(e) {
OpenDropdownForHash(location.hash, $(this).hasClass('dropdownTrigger'));
});
If clicking the link updates the hash, the e.preventDefault(); is not working. Check, that the links really get bound.
Seems like they are not. Try replacing e.preventDefault(); with alert('I am bound!!!'); and see what happens upon clicking the link.
Are you wrapping the code in document ready?
EDIT: If I understand it correctly, your anchor click handler is redundant, as clicking the link updates the hash itself.

Prevent 'click' event from firing multiple times + issue with fading

Morning folks. Have an issue with a simple jQuery gallery i'm making. It lets the user cycle through a collection of images via some buttons and at the same time, rotates through these images on a timer. My problem is that the user is able to click the button multiple times which queues up the fade in animation and repeats it over and over, e.g. user clicks button 5 times > same image fades in/out 5 times > gallery moves to next image.
I've tried using:
$('#homeGalleryImage li a').unbind('click');
After the click event is fired and then rebinding:
$('#homeGalleryImage li a').bind('click');
After it's done but this simply removes the click event after pressing a button once and never rebinds to it?
I've also tried disabling the button via:
$('#homeGalleryImage li a').attr('disabled', true);
To no avail... ?
There is a secondary issue where if you manage to click a button while the image is in a transition, the next image appears 'faded' as if the opacity has been lowered? Very strange... Here is the code for button clicks:
var i = 1;
var timerVal = 3000;
$(function () {
$("#homeGalleryControls li a").click(function () {
var image = $(this).data('image');
$('#galleryImage').fadeOut(0, function () {
$('#galleryImage').attr("src", image);
});
$('#galleryImage').fadeIn('slow');
$('.galleryButton').attr("src", "/Content/Images/Design/btn_default.gif");
$(this).find('img').attr("src", "/Content/Images/Design/btn_checked.gif");
i = $(this).data('index') + 1;
if (i == 4) {
i = 0;
}
timerVal = 0;
});
});
Here is the code that cycles through the images on a timer:
//Cycle through gallery images on a timer
window.setInterval(swapImage, timerVal);
function swapImage() {
$('#galleryImage').fadeOut(0, function () {
var imgArray = ["/Content/Images/Design/gallery placeholder.jpg", "/Content/Images/Design/1.jpg", "/Content/Images/Design/2.jpg", "/Content/Images/Design/3.jpg"];
var image = imgArray[i];
i++;
if (i == 4) {
i = 0;
}
$('#galleryImage').attr("src", image);
$('#galleryImage').fadeIn('slow');
});
var currentButton = $('#homeGalleryControls li a img').get(i - 1);
$('.galleryButton').attr("src", "/Content/Images/Design/btn_default.gif");
$(currentButton).attr("src", "/Content/Images/Design/btn_checked.gif");
}
I realise it might be a better idea to use a plugin but I'm very new to jQuery and I'd like to learn something rather than using some ready made code.
Any help at all, is much appreciated.
Thankyou
You could always try adding something to the element to cancel the click event?
For example
$(".element").click(function(e) {
if ( $(this).hasClass("unclickable") ) {
e.preventDefault();
} else {
$(this).addClass("unclickable");
//Your code continues here
//Remember to remove the unclickable class when you want it to run again.
}
}):
In your case you could try adding a check on the click.
$('#homeGalleryImage li a').attr('data-disabled', "disabled");
Then inside your click event
if ( $(this).attr("data-disabled" == "disabled") {
e.preventDefault();
} else {
//Ready to go here
}
Edit
Here is a working example showing the element becoming unclickable. http://jsfiddle.net/FmyFS/2/
if you want to make sure that the registered event is fired only once, you should use jQuery's one :
.one( events [, data ], handler ) Returns: jQuery
Description: Attach a handler to an event for the elements. The handler is executed at most once per element per event type.
see examples:
using jQuery: https://codepen.io/loicjaouen/pen/RwweLVx
// add an even listener that will run only once
$("#click_here_button").one("click", once_callback);
using vanilly JS: https://codepen.io/loicjaouen/pen/gOOBXYq
// add a listener that run only once
button.addEventListener('click', once_callback, {capture: true, once: true});

Prevent click event in jQuery triggering multiple times

I have created a jQuery content switcher. Generally, it works fine, but there is one problem with it. If you click the links on the side multiple times, multiple pieces of content sometimes become visible.
The problem most likely lies somewhere within the click event. Here is the code:
$('#tab-list li a').click(
function() {
var targetTab = $(this).attr('href');
if ($(targetTab).is(':hidden')) {
$('#tab-list li').removeClass('selected');
var targetTabLink = $(this).parents('li').eq(0);
$(targetTabLink).addClass('selected');
$('.tab:visible').fadeOut('slow',
function() {
$(targetTab).fadeIn('slow');
}
);
}
return false;
}
);
I have tried adding a lock to the transition so that further clicks are ignored as the transition is happening, but to no avail. I have also tried to prevent the transition from being triggered if something is already animating, using the following:
if ($(':animated')) {
// Don't do anything
}
else {
// Do transition
}
But it seems to always think things are being animated. Any ideas how I can prevent the animation being triggered multiple times?
One idea would be to remove the click event at the start of your function, and then add the click event back in when your animation has finished, so clicks during the duration would have no effect.
If you have the ability to execute code when the animation has finished this should work.
Add a variable to use as a lock rather than is(:animating).
On the click, check if the lock is set. If not, set the lock, start the process, then release the lock when the fadeIn finishes.
var blockAnimation = false;
$('#tab-list li a').click(
function() {
if(blockAnimation != true){
blockAnimation = true;
var targetTab = $(this).attr('href');
if ($(targetTab).is(':hidden')) {
$('#tab-list li').removeClass('selected');
var targetTabLink = $(this).parents('li').eq(0);
$(targetTabLink).addClass('selected');
$('.tab:visible').fadeOut('slow',
function() {
$(targetTab).fadeIn('slow', function(){ blockAnimation=false; });
}
);
}
}
return false;
}
);
Well this is how i did it, and it worked fine.
$(document).ready(function() {
$(".clickitey").click(function () {
if($("#mdpane:animated").length == 0) {
$("#mdpane").slideToggle("slow");
$(".jcrtarrow").toggleClass("arrow-open");
}
});
});
this is not doing what your code does ofcourse this is a code from my site, but i just like to point how i ignored the clicks that were happening during the animation. Please let me know if this is inefficient in anyway. Thank you.
I toyed around with the code earlier and came up with the following modification which seems to work:
$('#tab-list li a').click(
function() {
$('.tab:animated').stop(true, true);
var targetTab = $(this).attr('href');
if ($(targetTab).is(':hidden')) {
$('#tab-list li').removeClass('selected');
var targetTabLink = $(this).parents('li').eq(0);
$(targetTabLink).addClass('selected');
$('.tab:visible').fadeOut('slow',
function() {
$(targetTab).fadeIn('slow');
}
);
}
return false;
}
);
All that happens is, when a new tab is clicked, it immediately brings the current animation to the end and then begins the new transition.
one way would be this:
$('#tab-list ul li').one( 'click', loadPage );
var loadPage = function(event) {
var $this = $(this);
$global_just_clicked = $this;
var urlToLoad = $this.attr('href');
$('#content-area').load( urlToLoad, pageLoaded );
}
$global_just_clicked = null;
var pageLoaded() {
$global_just_clicked.one( 'click', loadPage );
}
As you can see, this method is fraught with shortcomings: what happens when another tab is clicked before the current page loads? What if the request is denied? what if its a full moon?
The answer is: this method is just a rudimentary demonstration. A proper implementation would:
not contain the global variable $global_just_clicked
not rely on .load(). Would use .ajax(), and handle request cancellation, clicking of other tabs etc.
NOTE: In most cases you need not take this round-about approach. I'm sure you can remedy you code in such a way that multiple clicks to the same tab would not affect the end result.
jrh.
One way to do this to use timeStamp property of event like this to gap some time between multiple clicks:
var a = $("a"),
stopClick = 0;
a.on("click", function(e) {
if(e.timeStamp - stopClick > 300) { // give 300ms gap between clicks
// logic here
stopClick = e.timeStamp; // new timestamp given
}
});

Categories

Resources