At the moment we are using the below script to create a lightweight lightbox for a wordpress site, which works perfectly on desktop, but the overlay is being triggered when just scrolling on a touch device if you scroll with your finger on one of the images, is there anyway to stop this happening?
<script>
var WHLightbox = {
settings: {
overlay: $('.portfolio-tile--overlay'),
imageCell: $('.cell-image, .portfolio-tile--image')
},
data: {
images: []
},
init: function() {
this.events();
this.buildImageData();
},
events: function() {
var self = this;
this.settings.imageCell.on('click touchend', function(e) {
e.preventDefault();
e.stopPropagation();
// set up the overlay
self._positionOverlay();
self._openOverlay();
self._preventScrolling();
// create the image slide
self._createImageSlide($(this));
});
this.settings.overlay.on('click touchend', function(e) {
e.preventDefault();
e.stopPropagation();
self._closeOverlay();
});
$('.portfolio-tile--overlay--controls--prev, .portfolio-tile--overlay--controls--next').on('click touchend', function(e) {
e.preventDefault();
e.stopPropagation();
});
$('.portfolio-tile--overlay--controls--prev').on('click touchend', function(e) {
e.preventDefault();
e.stopPropagation();
self.showPrev();
});
$('.portfolio-tile--overlay--controls--next').on('click touchend', function(e) {
e.preventDefault();
e.stopPropagation();
self.showNext();
});
},
// public functions
showPrev: function() {
var index = this.currentImageIndex();
if(index === 0) {
index = this.data.images.length;
}
this._createImageSlide(false, index-1);
},
showNext: function() {
var index = this.currentImageIndex();
if(index === this.data.images.length-1) {
// set to -1 because it adds 1 in the _createImageSlide call
index = -1;
}
this._createImageSlide(false, index+1);
},
currentImageIndex: function() {
if(this.settings.overlay.hasClass('open')) {
var imageUrl = $('.portfolio-tile--main-image').attr('src');
for(var i=0; i<this.data.images.length; i++) {
if(this.data.images[i].imageUrl === imageUrl) {
return i;
}
}
} else {
return false;
}
},
// image data
buildImageData: function() {
var self = this,
i = 0;
this.settings.imageCell.each(function() {
self.data.images[i] = {
imageUrl: self._getImagePath($(this))
}
i++;
});
},
// slide
_createImageSlide: function($el, index) {
var imagePath;
if(!$el) {
imagePath = this.data.images[index].imageUrl;
} else {
imagePath = this._getImagePath($el);
}
this.settings.overlay.find('.portfolio-tile--main-image').attr('src', imagePath);
},
_getImagePath: function($el) {
var imagePath,
spanEl = $el.find('span.js-cell-image-background'),
imgEl = $el.find('img.cell-image__image');
if(spanEl.length) {
imagePath = spanEl.css('backgroundImage');
imagePath = imagePath.replace('url(', '').replace(')', '');
} else if(imgEl.length) {
imagePath = imgEl.attr('src');
}
return imagePath;
},
// overlay
_positionOverlay: function() {
this.settings.overlay.css({
// position the overlay to current scroll position
top: $(window).scrollTop()
});
},
_openOverlay: function() {
this.settings.overlay.addClass('open');
},
_preventScrolling: function() {
$('html, body').addClass('no-scroll');
},
_reInitScrolling: function() {
$('html, body').removeClass('no-scroll');
},
_closeOverlay: function() {
this.settings.overlay.removeClass('open');
this._reInitScrolling();
}
};
WHLightbox.init();
</script>
You could use a library such as Modernizr to detect touch take a look at the answer to this question on detecting touch What's the best way to detect a 'touch screen' device using JavaScript?
With Modernizr only:
if (Modernizr.touch) {
alert('Touch Screen');
} else {
alert('No Touch Screen');
}
or with the use of jQuery & Modernizr you can do something like:
if($('html').hasClass('touch') === false) {
//run your code here
}
Thank you for your reply aharen, I managed to get it working by just changing touchend to tap and it seems to be working as intended now.
Related
I'm using Slit Slider js. This opens div slides with arrows and dots for navigation. Is there a way to create a link to open a specific slide? For example: Home, About Us etc. instead of arrows.
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
$(function() {
var Page = (function() {
var $navArrows = $('#nav-arrows'),
$nav = $('#nav-dots > span'),
slitslider = $('#slider').slitslider({
onBeforeChange: function(slide, pos) {
$nav.removeClass('nav-dot-current');
$nav.eq(pos).addClass('na```v-dot-current');
}
}),
init = function() {
initEvents();
},
initEvents = function() {
// add navigation events
$navArrows.children(':last').on('click', function() {
slitslider.next();
return false;
});
$navArrows.children(':first').on('click', function() {
slitslider.previous();
return false;
});
$nav.each(function(i) {
$(this).on('click', function(event) {
var $dot = $(this);
if (!slitslider.isActive()) {
$nav.removeClass('nav-dot-current');
$dot.addClass('nav-dot-current');
}
slitslider.jump(i + 1);
return false;
});
});
};
return {
init: init
};
})();
Page.init();
});
Thanks for your support :)
I need to fire the drop part so that when user drop a file it uploads. But in my code drop not firing.
Please give me the corrected version of this code. This is really very important for my project. This code is taken from another stackoverflow post. And I have edited the drop part at the code given below.
User's answer will be appreciated.
$.fn.dndhover = function(options) {
return this.each(function() {
var self = $(this);
var collection = $();
self.on('dragenter', function(event) {
if (collection.length === 0) {
self.trigger('dndHoverStart');
}
collection = collection.add(event.target);
});
self.on('dragleave', function(event) {
/*
* Firefox 3.6 fires the dragleave event on the previous element
* before firing dragenter on the next one so we introduce a delay
*/
setTimeout(function() {
collection = collection.not(event.target);
if (collection.length === 0) {
self.trigger('dndHoverEnd');
}
}, 1);
});
self.on('drop', function(event) {
alert(event);
self.trigger('dndHoverDrop');
});
});
};
$('.uploadDiv').dndhover().on({
'dndHoverStart': function(event) {
if(!$(".selectFile").hasClass("hide")){
$(".selectFile").addClass("hide");
}
if($(".dropFile").hasClass("hide")){
$(".dropFile").removeClass("hide");
}
event.stopPropagation();
event.preventDefault();
return false;
},
'dndHoverEnd': function(event) {
if($(".selectFile").hasClass("hide")){
$(".selectFile").removeClass("hide");
}
if(!$(".dropFile").hasClass("hide")){
$(".dropFile").addClass("hide");
}
event.stopPropagation();
event.preventDefault();
return false;
},
'dndHoverDrop': function(event) {
alert('dropped');
if(!$(".selectFile").hasClass("hide")){
$(".selectFile").addClass("hide");
}
if(!$(".dropFile").hasClass("hide")){
$(".dropFile").addClass("hide");
}
event.preventDefault();
event.stopPropagation();
return false;
}
});
New Code:
$.fn.dndhover = function(options) {
return this.each(function() {
var self = $(this);
var collection = $();
self.on('dragenter', function(event) {
if (collection.length === 0) {
self.trigger('dndDragEnter');
}
collection = collection.add(event.target);
});
self.on('dragleave', function(event) {
/*
* Firefox 3.6 fires the dragleave event on the previous element
* before firing dragenter on the next one so we introduce a delay
*/
setTimeout(function() {
collection = collection.not(event.target);
if (collection.length === 0) {
self.trigger('dndDragLeave');
}
}, 1);
});
self.on('dragover', function(event) {
setTimeout(function() {
collection = collection.not(event.target);
if (collection.length === 0) {
self.trigger('dndDragOver');
}
}, 1);
});
self.on('drop,dragdrop', function(event) {
self.trigger('dndDragDrop');
});
});
};
$('.uploadDiv').dndhover().on({
'dndDragEnter': function(event) {
if(!$(".selectFile").hasClass("hide")){
$(".selectFile").addClass("hide");
}
if($(".dropFile").hasClass("hide")){
$(".dropFile").removeClass("hide");
}
event.stopPropagation();
},
'dndDragLeave': function(event) {
if($(".selectFile").hasClass("hide")){
$(".selectFile").removeClass("hide");
}
if(!$(".dropFile").hasClass("hide")){
$(".dropFile").addClass("hide");
}
},
'dndDragDrop': function(event) {
alert('dropped');
if(!$(".selectFile").hasClass("hide")){
$(".selectFile").addClass("hide");
}
if(!$(".dropFile").hasClass("hide")){
$(".dropFile").addClass("hide");
}
alert(event);
event.preventDefault();
},
'dndDragOver': function(event) {
if(!$(".selectFile").hasClass("hide")){
$(".selectFile").addClass("hide");
}
if($(".dropFile").hasClass("hide")){
$(".dropFile").removeClass("hide");
}
event.preventDefault();
}
});
on my website I have a div .toggle-search that if you click on it it expands to .search-expand where a search form is. This is the code in jQuery
/* Toggle header search
/* ------------------------------------ */
$('.toggle-search').click(function(){
$('.toggle-search').toggleClass('active');
$('.search-expand').fadeToggle(250);
setTimeout(function(){
$('.search-expand input').focus();
}, 300);
});
Now the only way to close the .search-expand is to click once again on the .toggle-search. But I want to change that it closes if you click anywhere else on the site. For an easier example I have the Hueman theme, and I'm talking about the top right corner search option. http://demo.alxmedia.se/hueman/
Thanks!
Add the event on all elements except the search area.
$('body *:not(".search-expand")').click(function(){
$('.toggle-search').removeClass('active');
$('.search-expand').fadeOut(250);
});
or another way,
$('body').click(function(e){
if(e.target.className.indexOf('search-expand') < 0){
$('.toggle-search').removeClass('active');
$('.search-expand').fadeOut(250);
}
});
var isSearchFieldOpen = false;
var $toggleSearch = $('.toggle-search');
var $searchExpand = $('.search-expand');
function toggleSearch() {
// Reverse state
isSearchFieldOpen = !isSearchFieldOpen;
$toggleSearch.toggleClass('active');
// You can use callback function instead of using setTimeout
$searchExpand.fadeToggle(250, function() {
if (isSearchFieldOpen) {
$searchExpand.find('input').focus();
}
});
}
$toggleSearch.on('click', function(e) {
e.stopPropagation();
toggleSearch();
});
$(document.body).on('click', function(e) {
if (isSearchFieldOpen) {
var target = e.target;
// Checking if user clicks outside .search-expand
if (!$searchExpand.is(target) && !$searchExpand.has(target).length) {
toggleSearch();
}
}
});
I have a second search on the site with the same code as before only
with div .toggle-serach2 and .expand-search2, how can i make your code
so it wont overlap. just changing the name to $('toggle-search2')
doesn't cut it
in that case, I would suggest you convert your code into a plugin:
(function($, document) {
var bodyHandlerAttached = false;
var openedForms = [];
var instances = {};
var defaults = {
activeClass: 'active'
};
function ToggleSearch(elem, options) {
this.options = $.extend({}, defaults, options);
this.$elem = $(elem);
this.$btn = $(options.toggleBtn);
this.isOpen = false;
this.id = generateId();
this.bindEvents();
instances[this.id] = this;
if (!bodyHandlerAttached) {
handleOutsideClick();
bodyHandlerAttached = true;
}
}
ToggleSearch.prototype = {
bindEvents: function() {
this.$btn.on('click', $.proxy(toggleHandler, this));
},
open: function() {
if (this.isOpen) { return; }
var _this = this;
this.$btn.addClass(this.options.activeClass);
this.$elem.fadeIn(250, function() {
_this.$elem.find('input').focus();
});
openedForms.push(this.id);
this.isOpen = true;
},
close: function(instantly) {
if (!this.isOpen) { return; }
this.$btn.removeClass(this.options.activeClass);
if (instantly) {
this.$elem.hide();
} else {
this.$elem.fadeOut(250);
}
openedForms.splice(openedForms.indexOf(this.id), 1);
this.isOpen = false;
},
toggle: function() {
if (this.isOpen) {
this.close();
} else {
this.open();
}
}
};
var toggleHandler = function(ev) {
ev.stopPropagation();
this.toggle();
};
var handleOutsideClick = function(e) {
$(document.body).on('click', function(e) {
if (openedForms.length) {
var target = e.target;
var instance;
for (var id in instances) {
instance = instances[id];
if (!instance.$elem.is(target) && !instance.$elem.has(target).length) {
instance.close(true);
}
}
}
});
};
function generateId() {
return Math.random().toString(36).substr(2, 8);
}
$.fn.toggleSearch = function(options) {
return this.each(function() {
if (!$.data(this, 'toggleSearch')) {
$.data(this, 'toggleSearch', new ToggleSearch(this, options));
}
});
};
})(window.jQuery, document);
And then use it like this:
$('.search-expand').toggleSearch({
toggleBtn: '.toggle-search'
});
$('.search-expand2').toggleSearch({
toggleBtn: '.toggle-search2'
});
JSFiddle example
You could add a click handler to the main window that removes the active class:
$(window).click(function(){
$('.toggle-search').removeClass('active');
}
and then prevent the class removal when you click inside of your toggle-search elem
$('.toggle-search').click(function(e){
e.stopPropagation();
// remainder of click code here
)};
Try to add body click listener
$('body').click(function(e){
if ($(e.target).is('.toggle-search')) return;
$('.toggle-search').removeClass('active');
$('.search-expand').fadeOut(250);
});
Im trying to use hover with .on(), but all bits of code and answers i find do not work.
I need to use .on because its Ajax content.
A few ive tried:
$(document).on('mouseenter', '[rel=popup]', function() {
mouseMove = true;
width = 415;
$('#tool-tip').show();
var type = $(this).attr('data-type'),
id = $(this).attr('data-skillid');
console.log("TT-open");
ttAjax(type, id);
}).on('mouseleave', '[rel=popup]', function() {
mouseMove = false;
console.log("TT-close");
$('#tool-tip').hide();
$('#tt-cont').html("");
$('#tt-ajax').show();
});
$('[rel=popup]').on('hover',function(e) {
if(e.type == "mouseenter") {
mouseMove = true;
width = 415;
$('#tool-tip').show();
var type = $(this).attr('data-type'),
id = $(this).attr('data-skillid');
console.log("TT-open");
ttAjax(type, id);
}
else if (e.type == "mouseleave") {
mouseMove = false;
console.log("TT-close");
$('#tool-tip').hide();
$('#tt-cont').html("");
$('#tt-ajax').show();
}
});
$('[rel=popup]').on("hover", function(e) {
if (e.type === "mouseenter") { console.log("enter"); }
else if (e.type === "mouseleave") { console.log("leave"); }
});
$(document).on({
mouseenter: function () {
console.log("on");
},
mouseleave: function () {
console.log("off");
}
}, "[rel=popup]"); //pass the element as an argument to .on
The original non .on:
$('[rel=popup]').hover(function(){
mouseMove = true;
width = 415;
$('#tool-tip').show();
var type = $(this).attr('data-type'),
id = $(this).attr('data-skillid');
console.log("TT-open");
ttAjax(type, id);
},function () {
mouseMove = false;
console.log("TT-close");
$('#tool-tip').hide();
$('#tt-cont').html("");
$('#tt-ajax').show();
})
All the .on return "TypeError: $(...).on is not a function". I am using version 1.9.1.
The events you're looking for may be
$("#id").mouseover(function(){});
or
$("#id").mouseout(function(){});
There was an answer with:
$(document).on({
mouseenter: function () {
console.log("on");
},
mouseleave: function () {
console.log("off");
}
},"[rel=popup]");
This worked, and for some reason I have JQ 1.4 in a 1.9.1 named file that was causing the problem.
For some reason my gallery isn't working on Mobile devices including iPad, works fine on desktop. Instead of allowing a user to click through, all images appear stacked. The link to my site. The code is
located here
// scroll gallery init
function initCarousel() {
var isTouchDevice = /MSIE 10.*Touch/.test(navigator.userAgent) || ('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch;
jQuery('div.view-gallery').scrollGallery({
mask: 'div.frame',
slider: '>ul',
slides: '>li',
btnPrev: 'a.btn-prev',
btnNext: 'a.btn-next',
pagerLinks: '.pagination li',
circularRotation: false,
autoRotation: false,
switchTime: 3000,
animSpeed: 500,
onInit: function(obj){
obj.resizeFlag = true;
obj.win = jQuery(window);
//obj.win.unbind('resize orientationchange load', obj.onWindowResize);
obj.resizeSlides = function(){
obj.slideOffset = obj.slides.eq(0).outerWidth(true) - obj.slides.eq(0).width();
if(!obj.resizeFlag) obj.slides.css({width: ''});
else obj.slides.css({width: obj.mask.width()/2 - obj.slideOffset});
obj.calculateOffsets();
obj.refreshPosition();
obj.refreshState();
}
if(isTouchDevice){
ResponsiveHelper.addRange({
'..767': {
on: function(){
setTimeout(function(){
obj.resizeFlag = true;
obj.resizeSlides();
obj.win.bind('resize orientationchange load', obj.resizeSlides);
}, 100);
}
},
'768..': {
on: function(){
obj.resizeFlag = false;
obj.win.unbind('resize orientationchange load', obj.resizeSlides);
obj.resizeSlides();
}
}
});
}
}
});
jQuery('.scrollable-gallery').scrollableGallery();
}
/*
* scrollableGallery
*/
;(function($) {
function ScrollableGallery(options) {
this.options = {
scrollableArea: '.frame',
listItems: '.list-items',
btnPrev: '.btn-prev',
btnNext: '.btn-next',
animSpeed: 500
}
$.extend(this.options, options);
this.init();
}
ScrollableGallery.prototype = {
init: function() {
this.findElements()
this.setStructure();
this.addEvents();
},
findElements: function() {
this.holder = $(this.options.holder);
this.scrollableArea = this.holder.find(this.options.scrollableArea);
this.listItems = this.scrollableArea.find(this.options.listItems);
this.items = this.listItems.children();
this.lastItem = this.items.last();
this.btnPrev = this.holder.find(this.options.btnPrev);
this.btnNext = this.holder.find(this.options.btnNext);
this.scrollAPI = new jcf.modules.customscroll({
replaces: this.scrollableArea[0]
});
},
setStructure: function() {
var that = this;
if (that.listItems.css('position') === 'static') {
that.listItems.css('position', 'relative');
}
setTimeout(function() {
that.refreshState();
}, 50);
},
refreshState: function() {
this.listItems.css('width', 32700);
this.listItems.css('width', this.lastItem.position().left + this.lastItem.outerWidth(true) + 1);
this.scrollableArea.add(this.scrollableArea.parent()).css({
width: '',
height: ''
});
this.scrollAPI.refreshState();
},
addEvents: function() {
var that = this;
that.btnPrev.bind('click', function(e) {
e.preventDefault();
that.prevSlide();
});
that.btnNext.bind('click', function(e) {
e.preventDefault();
that.nextSlide();
});
win.bind('resize orientationchange load', function() {
that.refreshState();
});
},
nextSlide: function() {
var that = this;
var curPos = this.scrollableArea.scrollLeft();
var pos;
for (var i = 0; i < that.items.length; i++) {
pos = that.items.eq(i).position().left;
if (pos > curPos) {
that.scrollAnimate(curPos, pos);
break;
}
}
},
prevSlide: function() {
var that = this;
var curPos = this.scrollableArea.scrollLeft();
var pos;
for (var i = that.items.length - 1; i >= 0; i--) {
pos = that.items.eq(i).position().left;
if (pos < curPos) {
that.scrollAnimate(curPos, pos);
break;
}
}
},
scrollAnimate: function(from, to) {
var that = this;
var start = new Date().getTime();
setTimeout(function() {
var now = (new Date().getTime()) - start;
var progress = now / that.options.animSpeed;
var result = (to - from) * progress + from;
that.scrollAPI.hScrollBar.scrollTo(result);
if (progress < 1) {
setTimeout(arguments.callee, 10);
} else {
that.scrollAPI.hScrollBar.scrollTo(to);
}
}, 10);
}
}
var win = $(window);
$.fn.scrollableGallery = function(options) {
return this.each(function() {
if (!$(this).data('ScrollableGallery')) {
$(this).data('ScrollableGallery', new ScrollableGallery($.extend({}, {holder: this}, options)));
}
});
}
}(jQuery));
After looking through your code, there were numerous errors with syntax. I have cleaned them up as best as I could, this should help you out.
http://jsfiddle.net/wvWrY/1/
For example, this area was missing a semicolon (no way to call the findElements function, as JS will simply skip to the next line without a semicolon there.)
init: function() {
this.findElements()
this.setStructure();
this.addEvents();
Run your code through a linter, it will greatly improve your syntax structure and ensure little leave out errors like semicolons and commas and brackets aren't omitted.
EDIT: Ok, having looked at your code it appears this is actually due to the !importants in your allmobile.css file. The width and height are set to max-width: 100% (this breaks it because the way the slider works is to extend the gallery as far off screen as possible) and the height to auto (this breaks it because it allows the images to just keep piling on). Once you remove those for the page, it become much much much better and actually works.