Allow touchmove for child element - javascript

I want to disable scrolling for my entire jqm web app but if you try to scroll on a div with a certain class, then scrolling is allowed
So my scrolling div, .info has overflow:scroll applied to it and I have this script to try to detect if you're touching it
$('.info').bind('touchstart', function (e) {
if (e.type == "touchstart") {
return true;
} else {
event.preventDefault();
}
});
$(document).bind('touchmove', function () {
event.preventDefault();
})
Right now, scrolling is turned off for the document but when I try to scroll my info div, it won't scroll.
Now I've seen a few post around this same idea, but mostly about to pinning an objects position so that when the document is scrolled, it stays in the same place.

This is how I've been doing it:
$(document).on('touchmove', function(ev) {
if ( !$(ev.target).closest('.is-scrollable').length ) {
ev.preventDefault();
}
})
Where .is-scrollable would be your class, .info in this case.
EDIT: to fix scrolling at top and bottom of scrollable div:
$('.is-scrollable').on('touchstart', function() {
var el = $(this);
if ( el.scrollTop() <= 0 ) {
el.scrollTop(1);
}
if ( el.scrollTop() >= el[0].scrollHeight ) {
el.scrollTop(el[0].scrollHeight - 1);
}
});

Related

Restore scroll position after mobile menu is closed

I'm having a mobile menu that opens and closes using jquery by adding a css class that has display:block while the menu div has display:none.
The jquery code has a part where it is supposed to close the menu when a click is registered outside the menu div. Everything works execept the: $("body").scrollTop(scrollpos) . This was supposed to scroll the user back where he left off after the scrollTop(0) took place and the menu has closed, but it does not scroll at all the scroll is stuck at the top. Any help will be appreciated. Thank you.
EDIT: Here is an example: https://jsfiddle.net/mufwwudj/
$(function () {
var menutoggle = $(".menu-toggle");
var sidenav = $(".side-nav");
menutoggle.click(function () {
var scrollpos = $('body').scrollTop();
if (!$("body").hasClass("m-nav-open")) {
$("body").scrollTop(0).addClass("m-nav-open");
}
$(document).mouseup(function (e){
if (!sidenav.is(e.target) && sidenav.has(e.target).length === 0 && !menutoggle.is(e.target) && menutoggle.has(e.target).length === 0){
if ($("body").hasClass("m-nav-open")) {
$("body").scrollTop(scrollpos).removeClass("m-nav-open");
}
}
});
});
});
One problem here is that you are assigning a new mouseup event every time the menutoggle.click function runs.
$(document).mouseup(function (e){
if (!sidenav.is(e.target) && sidenav.has(e.target).length === 0 && !menutoggle.is(e.target) && menutoggle.has(e.target).length === 0){
if ($("body").hasClass("m-nav-open")) {
$("body").scrollTop(scrollpos).removeClass("m-nav-open");
}
}
});
Only the first one passes the conditional, even though each one will fire and scrollpos will always equal whatever it was in the first mouseup event listener.
I don't know how you are testing it, or what the HTML looks like but if you are at the top of the page the first time you click it, scrollpos in the mouseup event will always be 0.
Try assigning the mouseup event once, and putting scrollpos outside both so it can be accessed in both.
$(function () {
var menutoggle = $(".menu-toggle");
var sidenav = $(".side-nav");
var scrollpos;
menutoggle.click(function () {
scrollpos = $('body').scrollTop();
if (!$("body").hasClass("m-nav-open")) {
$("body").scrollTop(0).addClass("m-nav-open");
}
});
$(document).mouseup(function (e){
if (!sidenav.is(e.target) && sidenav.has(e.target).length === 0 && !menutoggle.is(e.target) && menutoggle.has(e.target).length === 0){
if ($("body").hasClass("m-nav-open")) {
$("body").scrollTop(scrollpos).removeClass("m-nav-open");
}
}
});
});
function ScrollOnTopo() {
window.scrollTo(0, 0); //It scrolls page at top
}
This function may be useful to you.

Why my fix menu in not working smoothly on scrolling.

$(window).scroll(function() {
var windscroll = $(window).scrollTop();
if (windscroll >= 5) {
$('#page-header').addClass('fixed');
} else {
$('#page-header').removeClass('fixed');
}
}).scroll();
Why my fix menu in not working smoothly on scrolling. i am using in my moodle theme frontpage.php or i have to add some thing for smoothness.
You're modifying the DOM too frequently, as $(window).scroll fires multiple times in a single scroll. Consider checking the existence of the class before add or remove it.
$(window).scroll(function() {
var windscroll = $(window).scrollTop();
if (windscroll >= 5) {
if(!$('#page-header').hasClass('fixed')) {
$('#page-header').addClass('fixed');
}
} else {
if(!$('#page-header').hasClass('fixed')) {
$('#page-header').removeClass('fixed');
}
}
});
also, I removed an extra .scroll() call at the end of the script.
Alternatively, you can make use of a jQuery On Screen plugin which will add a pseudo class :onscreen with the div visible in the browser. Codes are as follow:
$(document).scroll(function() {
if($("#page-header").is(':onScreen')) {
console.log("Element appeared on Screen");
if(!$('#page-header').hasClass('fixed')) {
$('#page-header').addClass('fixed');
}
} else {
console.log("Element not on Screen");
if(!$('#page-header').hasClass('fixed')) {
$('#page-header').removeClass('fixed');
}
}
});
See if the plugin fits your needs.

jQuery Undelegate does not work

I'm creating a responsive template and I want to remove the listeners on an element when screen is being resized or is smaller than the specified width.
Imagine an menu which when you hover on it's items, it shows you the sub-menus in normal displays but the same menu in mobile devices will show the sub-menus only by tapping or clicking on the items.
I can't make the undelegate work. In resized screen I still have the mouseover and mouseout event-listeners. I'm not getting any errors in console and I've tried both:
.off('mouseover', 'li')
.off('mouseover')
.undelegate('li', 'mouseover')
.undelegate('li')
and none of them works.
var $window = $(window);
function handleSidenav() {
$(".nav-list").delegate('li', 'mouseover', function(e) {
$(this).find("a").addClass('active');
$(this).find("div.sub-items").toggle();
}).delegate('li', 'mouseout', function(e) {
$(this).find('a').removeClass('active');
$(this).find("div.sub-items").toggle();
});
}
function checkWidth() {
var windowsize = $window.width();
if (windowsize < 767) {
smallScreenDelegation();
} else {
SmallScreenUndelegation();
}
}
checkWidth();
handleSidenav();
$window.resize(checkWidth());
function smallScreenDelegation() {
$(".nav-list").undelegate('li'); //It's not working
$(".nav-list").undelegate('li'); //It's not working
$(".nav-list").delegate('li a:first', 'click', function(event) {
if ($(this).next().is(':hidden')) {
$(this).addClass('active');
$(this).next().slideDown('slow');
} else {
$(this).removeClass('active').next().slideUp('slow');
}
event.preventDefault();
});
}
You need to wrap window in the jQuery object. I'm not sure if you set $window = $(window), but it seems here that $window.width() and $window.resize(checkWidth) are missing parenthesis. I was able to get it working fine once I changed those to $(window). You have to define which event you want to undelegate. I used:
$('.nav-list').undelegate('li', 'mouseover');
Open up console and you can see that it works: http://jsbin.com/efonut/6/edit
Also, it's really best to use .on() and off() vs .delegate() and .undelegate(), but at least this works...
I still don't know what was wrong with undelegate which I couldn't make it work, but I managed to fix my code by using on and off.
As adeneo said I was delegating and undelegating on each window resize which was quiet a bug and I think I fixed that but holding the last state on device variable.
var $window = $(window);
var device;
function desktopSidenav() {
$(".nav-list > li").off('click');
$(".nav-list > li").on('mouseover', function(e) {
$(this).find("a").addClass('active');
$(this).find("div.sub-items").toggle();
}).on('mouseout', function(e) {
$(this).find('a').removeClass('active');
$(this).find("div.sub-items").toggle();
});
}
function handheldSidenav() {
$(".nav-list > li").off('mouseover').off('mouseout');
$(".nav-list > li").on('click', function(e) {
if ($(this).find("div.sub-items").is(':hidden')) {
$(this).find("a:first").addClass('active').next().slideDown('slow');
} else {
$(this).find("a:first").removeClass('active').next().slideUp('slow');
}
e.preventDefault();
});
}
Now I check the window size before doing anything else an I'll hold the device type in device variable. If window is resized, I'm gonna check the device state and do the things based on device type.
if ($window.width() > 767) {
device = 'desktop';
desktopSidenav();
} else {
device = 'handheld';
handheldSidenav();
}
$window.resize(function() {
if ($window.width() > 767) {
if (device == 'handheld') {
device = 'desktop';
desktopSidenav();
}
} else {
if (device == 'desktop') {
device = 'handheld';
handheldSidenav();
}
}
});
If I use delegate and undelegate instead of on and off, the code won't work and I still don't know why, so this cannot be count as a real answer, but I wanted to tell everyone who has a similar problem to use jQuery's on and off instead on delegate.

Catch scrolling event on overflow:hidden element

Any insights on how to catch a scrolling event on a element that has overflow:hidden? I would like to scroll in a column without showing a scrollbar to the user.
This is actually a somewhat indepth process. What I do is set global flags when users mouse enters and leaves the element that you want to scroll. Then, on the mousewheel event for the body I check to see if the MOUSE_OVER flag is true, then stop propagation of the event. This is so the main body doesnt scroll in case your entire page has overflow.
Note that with overflow hidden, the default scrolling ability is lost so you must create it yourself. To do this you can set a mousewheel listener on your div in question and use the event.wheelDelta property to check whether the user is scrolling up or down. This value is different according to browser, but it is generally negative if scrolling down and positive if scrolling up. You can then change position of your div accordingly.
This code is hacked up quickly but it would essentially look like this...
var MOUSE_OVER = false;
$('body').bind('mousewheel', function(e){
if(MOUSE_OVER){
if(e.preventDefault) { e.preventDefault(); }
e.returnValue = false;
return false;
}
});
$('#myDiv').mouseenter(function(){ MOUSE_OVER=true; });
$('#myDiv').mouseleave(function(){ MOUSE_OVER=false; });
$('#myDiv').bind('mousewheel', function(e){
var delta = e.wheelDelta;
if(delta > 0){
//go up
}
else{
//go down
}
});
I use overflow:scroll, but also Absolutely position a div over the scroll bar in order to hide it.
$("body").css("overflow", "hidden")
$(document).bind('mousewheel', function(evt) {
var delta = evt.originalEvent.wheelDelta
console.log(delta)
})
works for me. adapted from How do I get the wheelDelta property?
I edited #anson s answer to Vanilla Javascript since it may be useful for others. Also note that "mousewheel" event is deprecated. So my code uses "wheel" instead. Next to that I added arrow functions for practical access the to "this".
fixScrollBehavior(elem) {
elem.addEventListener('scroll', (e) => {
console.log('scrolling');
});
let MOUSE_OVER = false;
elem.addEventListener('wheel', (e) => {
if (MOUSE_OVER) {
if (e.preventDefault) {
e.preventDefault();
}
e.returnValue = false;
return false;
}
});
elem.addEventListener('mouseenter', () => {
MOUSE_OVER = true;
});
elem.addEventListener('mouseleave', () => {
MOUSE_OVER = false;
});
elem.addEventListener('wheel', (e) => {
let delta = e.wheelDelta;
if (delta > 0) {
//go up
} else {
//go down
}
});
}
Note that this does not fix the mobile touch-"scroll"s.
$("div").on('wheel', function (e) {
if (e.originalEvent.deltaY < 0) {
console.log("Scroll up");
} else {
console.log("Scroll down");
}
});
This did the trick for me.
JSFiddle
StackFiddle:
$("div").on('wheel', function(e) {
if (e.originalEvent.deltaY < 0) {
console.log("Scroll up");
} else {
console.log("Scroll down");
}
});
div {
height: 50px;
width: 300px;
background-color: black;
overflow: hidden;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div></div>
I am late, but I think I have a better answer.
Style your container as overflow: overlay, this will free up space of scrollbar, then style scrollbar or hide it or make its handle height/width 0,
Then you should get scroll events also.
Note : styling the scrollbar is not supported in all web browsers.

prevent Scroll bubbling from element to window

I have a modal box window (pop-up) that contains an iframe,
and inside that iframe there's a div that is scrollable.
When I scroll the iframe's inner DIV, and it has reached its top or bottom limit, the window of the browser itself starts to scroll. this is an unwanted behavior.
I've tried something like this, which kills the main window scroll when onMouseEnter when mouse enters pop-up box area:
e.preventDefault() is not working as it should for some reason...
$("#popup").mouseenter(function(){
$(window).bind("scroll", function(e){
e.preventDefault();
});
}).mouseleave(function(){
$(window).unbind("scroll");
});
Solved (for some browsers) using a simple CSS property: overscroll-behavior:
auto
The default scroll overflow behavior occurs as normal.
contain
Default scroll overflow behavior is observed inside the element this value is set on (e.g. "bounce" effects or refreshes), but no scroll chaining occurs to neighboring scrolling areas, e.g. underlying elements will not scroll.
none
No scroll chaining occurs to neighboring scrolling areas, and default scroll overflow behavior is prevented.
body{
height: 600px;
overflow: auto;
}
section{
width: 50%;
height: 50%;
overflow: auto;
background: lightblue;
overscroll-behavior: none; /* <--- the trick */
}
section::before{
content: '';
height: 200%;
display: block;
}
<section>
<input value='end' />
</section>
Simply apply that style property on the element which the scroll should be "locked-in" to and the scroll event will not bubble up to any parent element which might have a scroll as well.
Same demo as above but without the trick:
body{
height: 600px;
overflow: auto;
}
section{
width: 50%;
height: 50%;
overflow: auto;
background: lightblue;
}
section::before{
content: '';
height: 200%;
display: block;
}
<section>
<input value='end' />
</section>
Sorry, as far as I'm aware it is impossible to cancel any kind of scroll event.
Both W3 and MSDN say:
Cancelable No
Bubbles No
I think you'll have to leave this up to browser authors to fix. Firefox (3.5 on Linux, anyway) seems to have a better behaviour for me: it only scrolls the parent if the child is already at the top/bottom end at the moment you start using the scrollwheel.
If we cannot prevent window scrolling, why not undo it?
That is, catching the scroll event and then scrolling back to a fixed position.
The following code locks the Y-Axis as long as one hovers over $("#popup"):
// here we store the window scroll position to lock; -1 means unlocked
var forceWindowScrollY = -1;
$(window).scroll(function(event) {
if(forceWindowScrollY != -1 && window.scrollY != forceWindowScrollY) {
$(window).scrollTop(forceWindowScrollY);
}
});
$("#popup").hover(function() {
if(forceWindowScrollY == -1) {
forceWindowScrollY = $(window).scrollTop();
}
}, function() {
forceWindowScrollY = -1;
});
I use this for the query suggest box on http://bundestube.de/ (enter some characters into the top search box to make the scrollable pane visible):
This works flawlessly in Chrome/Safari (Webkit) and with some scrolling glitches in Firefox and Opera. For some reason, it does not work with my IE installation. I guess this has to do with jQuery's hover method, which appears to not work correctly in 100% of all cases.
I know it's quite an old question, but since this is one of top results in google... I had to somehow cancel scroll bubbling without jQuery and this code works for me:
function preventDefault(e) {
e = e || window.event;
if (e.preventDefault)
e.preventDefault();
e.returnValue = false;
}
document.getElementById('a').onmousewheel = function(e) {
document.getElementById('a').scrollTop -= e. wheelDeltaY;
preventDefault(e);
}
my jQuery plugin:
$('.child').dontScrollParent();
$.fn.dontScrollParent = function()
{
this.bind('mousewheel DOMMouseScroll',function(e)
{
var delta = e.originalEvent.wheelDelta || -e.originalEvent.detail;
if (delta > 0 && $(this).scrollTop() <= 0)
return false;
if (delta < 0 && $(this).scrollTop() >= this.scrollHeight - $(this).height())
return false;
return true;
});
}
As of now in 2018 and onwards e.preventDefault is enough.
$('.elementClass').on("scroll", function(e){
e.preventDefault();
});
This will prevent scroll to parent.
That's how I solved the problem:
I call the following when I open the popup:
$('body').css('overflow','hidden');
Then, when I close the popup I call this:
$('body').css('overflow','auto');
The popup is meant to be modal so no interaction is required with the underlying body
Works pretty well
Apparently, you can set overflow:hidden to prevent scrolling. Not sure how that'd go if the doc is already scrolled. I'm also on a mouseless laptop, so no scrolly wheel testing for me tonight :-) It's probably worth a shot though.
you can try jscroll pane inside the iframe to replace the default scroll.
http://www.kelvinluck.com/assets/jquery/jScrollPane/jScrollPane.html
I am not sure, but give it a try
Here's what I do:
$('.noscroll').on('DOMMouseScroll mousewheel', function(ev) {
var prevent = function() {
ev.stopPropagation();
ev.preventDefault();
ev.returnValue = false;
return false;
}
return prevent();
});
demo fiddle
Use CSS overflow:hidden to hide the scrollbar as this will do nothing if they drag it.
Works cross-browser
New web dev here. This worked like a charm for me on both IE and Chrome.
static preventScrollPropagation(e: HTMLElement) {
e.onmousewheel = (ev) => {
var preventScroll = false;
var isScrollingDown = ev.wheelDelta < 0;
if (isScrollingDown) {
var isAtBottom = e.scrollTop + e.clientHeight == e.scrollHeight;
if (isAtBottom) {
preventScroll = true;
}
} else {
var isAtTop = e.scrollTop == 0;
if (isAtTop) {
preventScroll = true;
}
}
if (preventScroll) {
ev.preventDefault();
}
}
}
Don't let the number of lines fool you, it is quite simple - just a bit verbose for readability (self documenting code ftw right?)
I would like to add a bit updated code that I found to work best:
var yourElement = $('.my-element');
yourElement.on('scroll mousewheel wheel DOMMouseScroll', function (e) {
var delta = e.originalEvent.wheelDelta || -e.originalEvent.detail;
if (delta > 0 && $(this).scrollTop() <= 0)
return false;
if (delta < 0 && $(this).scrollTop() >= this.scrollHeight - $(this).outerHeight())
return false;
return true;
});
The difference between this one and one that is already mentioned above is the addition of more events and the usage of outerHeight() instead of height() to avoid crashing if element has padding!
$('.scrollable').on('DOMMouseScroll mousewheel', function (e) {
var up = false;
if (e.originalEvent) {
if (e.originalEvent.wheelDelta) up = e.originalEvent.wheelDelta / -1 < 0;
if (e.originalEvent.deltaY) up = e.originalEvent.deltaY < 0;
if (e.originalEvent.detail) up = e.originalEvent.detail < 0;
}
var prevent = function () {
e.stopPropagation();
e.preventDefault();
e.returnValue = false;
return false;
}
if (!up && this.scrollHeight <= $(this).innerHeight() + this.scrollTop + 1) {
return prevent();
} else if (up && 0 >= this.scrollTop - 1) {
return prevent();
}
});
Try the below code:
var container = document.getElementById('a');
container.onwheel = (e) => {
const deltaY = e.wheelDeltaY || -(e.deltaY * 25); // Firefox fix
container.scrollTop -= deltaY;
e.preventDefault();
e.stopPropagation();
e.returnValue = false;
};
function stopPropogation(e)
{
e = e || window.event;
e.cancelBubble = true;
if (e.stopPropagation) e.stopPropagation();
if (e.preventDefault) e.preventDefault();
}
This should work.

Categories