Prevent Scroll Script
// left: 37, up: 38, right: 39, down: 40,
// spacebar: 32, pageup: 33, pagedown: 34, end: 35, home: 36
var keys = [37, 38, 39, 40];
function preventDefault(e) {
e = e || window.event;
if (e.preventDefault)
e.preventDefault();
e.returnValue = false;
}
function keydown(e) {
for (var i = keys.length; i--;) {
if (e.keyCode === keys[i]) {
preventDefault(e);
return;
}
}
}
function wheel(e) {
preventDefault(e);
}
function disable_scroll() {
if (window.addEventListener) {
window.addEventListener('DOMMouseScroll', wheel, false);
}
window.onmousewheel = document.onmousewheel = wheel;
document.onkeydown = keydown;
}
function enable_scroll() {
if (window.removeEventListener) {
window.removeEventListener('DOMMouseScroll', wheel, false);
}
window.onmousewheel = document.onmousewheel = document.onkeydown = null;
}
Usage
Call disable_scroll(); to disable the page scrolling and enable_scroll() to enable the scrolling once again.
The Problem
Unlike the Facebook modal box, you are still able to click and drag the page to scroll down.
JSFiddle
Link: http://jsfiddle.net/2rud0aLm/
#Terry's first sentence provides a quick solution. Simply change overflow to 'hidden' on the body to prevent scrolling.
You will also need to keep track of the window's scrolled position, and set it after changing the overflow property.
To prevent the mousewheel from being able to drag, attach a scroll event to the window, which sets scrollTop to the window's position when the modal dialog was opened:
function disable_scroll() {
var top= $(window).scrollTop();
$('body').css({
overflow: 'hidden'
});
$(window).scrollTop(top);
$(window).on('scroll', function() {
$(window).scrollTop(top);
});
}
function enable_scroll() {
var top= $(window).scrollTop();
$('body').css({
overflow: ''
});
$(window).scrollTop(top);
$(window).off('scroll');
}
Because modal_close and modal_2 in your code has href="#", the script will attempt to jump to the top of the page. You can prevent that using preventDefault:
$('a[href=#]').on('click', function(ev) {
ev.preventDefault();
});
Fiddle
Here is a rather rudimentary fix, and I will explain what I have changed in order to make it work:
CSS: For the overlay, you actually do not need to sniff the viewport dimensions. Simply setting to position: fixed with all four offsets, top, left, bottom, and right set to 0 will force it to fill the screen :)
Markup: Wrap all your page content in a container, say <div class="page-wrap">. This element is set to fixed position upon toggling of the modal box to prevent click-drag scrolling.
JS:
Set a global variable as fromTop, which we will use to track the user's scroll position.
When the modal box is opened, update scroll position. Hide vertical overflow of the body element, and vertically translate the entire page content, i.e. .page-wrap to preserve vertical location
When the modal box is closed, reverse what we have done above :) I have decided to use a callback at the end of .fadeOut() to prevent jerking.
With all that done, you don't even need to prevent the scroll event from firing, or listening to keypress events anymore. Without further ado, here is the code (here's the functional demo):
var fromTop;
$('.modal_2').click(function(){
// Disable scroll and fade in modal box
disable_scroll();
$('.block_page').fadeIn();
$('.modal_box').fadeIn();
// Fetch current scroll position
fromTop = $(window).scrollTop();
// Hide overflowing vertical content
$('body').css({
'overflow-y': 'hidden'
});
$('.page-wrap').css({
'transform': 'translateY(-'+fromTop+'px)'
});
});
$('.modal_close').click(function(e){
e.preventDefault();
// Enable scroll and fade out modal box
$('.block_page').fadeOut(function() {
// Wait for modal box to fade out before reversing things
// Hide overflowing vertical content
$('body').css({
'overflow-y': 'visible'
});
$('.page-wrap').css({
'transform': 'translateY(0)'
});
$(window).scrollTop(fromTop);
});
$(this).parent().fadeOut();
enable_scroll();
});
Proof-of-concept fiddle: http://jsfiddle.net/teddyrised/mjq8gv29/
Even better: use jQuery promises to check if fadeOut animations have been completed on both the .block_page element and the parent element. This is exceptionally important if you want to set variable animation durations for either elements:
$('.modal_close').click(function(e){
e.preventDefault();
// Enable scroll and fade out modal box
$('.block_page').fadeOut();
$(this).parent().fadeOut();
// Use jQuery promises to check if all fadeOut animation has completed
var p1 = $('.block_page').promise(),
p2 = $(this).parent().promise();
// When all animations have completed, reverse effects
$.when(p1, p2).done(function() {
// Hide overflowing vertical content
$('body').css({
'overflow-y': 'visible'
});
$('.page-wrap').css({
'transform': 'translateY(0)'
});
$(window).scrollTop(fromTop);
});
});
Advanced fiddle that uses jQuery .promise() deferred object: http://jsfiddle.net/teddyrised/2rud0aLm/6/
Related
I've written a function which makes my top-bar ($header) fixed if you scroll down on device ( < 992 ) which works fine. And when I resize, I use the function fixedTopCheck() again and it removes the .fixed class (in the else statement). This also works fine. But then when I scroll, it suddendly gives top the class .fixed again. So this part is getting ignored if ($(window).width() < 992) (which only happens when resizing, if I refresh with a window above 992px it works fine).
So is this resizing messing with the recognition of $(window).width()? (When I console log it, it shows the correct width size).
My code:
$(function()
{
var $header = $('header.top');
var $input = $header.find('input[type=search]');
var $search = $header.find('div.search');
var $container = $('main#content');
var $searchBtn = $search.find('button.icon-search');
var $closeBtn = $search.find('span.icon-cross');
$closeBtn.css('display', 'none');
function fixedTopCheck()
{
if ($(window).width() < 992)
{
$(window).on('scroll', function()
{
if ($(window).scrollTop() > 75)
{
$searchBtn.css('display', 'inline-block');
$closeBtn.css('display', 'none');
// Turn top into fixed menu
$header.addClass('fixed');
$container.addClass('fixed');
// Hide search bar and make it smaller
$input.css('display', 'none');
// Prevent search button to search
$searchBtn.on('click', function(e) {
e.preventDefault();
});
// Open search bar when clicking button
$(document).on('click','.icon-search',function()
{
$searchBtn.unbind('click');
$input.css('display', 'inline-block');
$searchBtn.css('display', 'none');
$closeBtn.css('display', 'inline-block');
$input.focus();
});
// Close search bar when clicking button
$(document).on('click','.icon-cross',function()
{
$searchBtn.on('click', function(e)
{
e.preventDefault();
});
$input.css('display', 'none');
$searchBtn.css('display', 'inline-block');
$closeBtn.css('display', 'none');
});
}
else
{
// Reverse fixed menu
$header.removeClass('fixed');
$container.removeClass("fixed");
$input.css('display', 'inline-block');
// Return search function
$searchBtn.unbind('click');
// Reset search form when going top and search form is still opened
$searchBtn.css('display', 'inline-block');
$closeBtn.css('display', 'none');
}
});
}
else
{
console.log("hello");
// Remove fixed top
if($header.hasClass("fixed"))
{
$header.removeClass("fixed");
}
if($container.hasClass("fixed"))
{
$container.removeClass("fixed");
}
$searchBtn.css('display', 'inline-block');
$closeBtn.css('display', 'none');
}
}
fixedTopCheck();
// if window is resized check again
$( window ).resize(function() {
fixedTopCheck();
});
});
Your code is following a lot of bad practices.
dont nest your event listeners inside each other. this will cause setting an event listener on the same component everytime its container event triggers (on each resize is VERY heavy aswell).
instead of using bind and unbind, make a check the width inside each event and follow actions depending on that.
another solution for replacing bind and unbind in your case is to keep the default button events, but disable the button so it cannot be clicked instead of unbind.
This will answer your question:
adding events inside resize, will not make them only work on that size in the if statement, you have to put the event outside the resize event and check whenever that event happens on the scroll and size.
When the page is loaded and the width of the page is <992px, the scroll event is created. When you resize to >992px the scroll event will still exist.
I would recommend binding the scroll event only once on page load (not after each resize) and check the width inside of that method to decide what it should do when it's scrolling.
I was wondering if I can detect native scrollbar's left/right or up/down buttons click event. I want to know because I want to give a custom behaviour of the scrollbar, like scroll only in some fixed steps etc. I have used scroll function, but it doesn't exactly give me the smoothness I wanted:
var step = 200;
var nextPos=-1;
var pos;
var scrolltarget;
$(' .scrollable ul').scroll(function(e){
clearTimeout($.data(this, 'scrollTimer'));
scrolltarget=$(this);
$.data(this, 'scrollTimer', setTimeout(function() {
pos=scrolltarget.scrollLeft();
if(pos%step!=0){
if(pos>nextPos){
scrolltarget.animate({
scrollLeft: pos-pos%step+step
},250);
}
else if(pos<nextPos){
scrolltarget.animate({
scrollLeft: pos-pos%step
},250);
}
}
nextPos=scrolltarget.scrollLeft();
}, 250));
});
you can use
$( window ).scroll(function() {
// your java script code
});
i recommend you to see this scroll jquery event.
function dropdownHover() {
jQuery('ul.nav li.dropdown').hover(function() {
jQuery(this).find('.dropdown-menu').stop(true, true).delay(200).fadeIn();
}, function() {
jQuery(this).find('.dropdown-menu').stop(true, true).delay(200).fadeOut();
});
}
$(window).on('resize', function(event){
var windowSize = $(window).width();
if(windowSize > 992){
dropdownHover();
}
});
I need this function dropdownHover() to fire only when window is greater than 992px, both on load and on resize, else if window is < 992px, both on load or on resize i dont want to fire this function on hover i want regular bootstrap dropdown on click. I tried to do this with css but i cant add animation on dropdown because its just display: none/block. I also tried to add class on resize to fire this function if element has that class else dont but it doesnt work either.
Edit: Final working version
$('.dropdown').on('mouseenter', function(){
if(!$(this).is('.open') && $(window).data('wide'))
$('.dropdown-menu', this).dropdown('toggle').hide()
.stop(true, true)
.delay(200)
.fadeIn(function(){
this.style.display = '';
}).find('a').on('touchstart click', function (e) {
e.stopPropagation();
});
}).on('mouseleave', function(){
if($(this).is('.open') && $(window).data('wide'))
$('.dropdown-menu', this).dropdown('toggle');
});
$('.dropdown').on('click', function(e){
if( $(window).data('wide')) {
$('.dropdown-menu', this).dropdown('toggle');
} else {
$('.dropdown-menu', this)
.stop(true, true).slideToggle()
.closest('.dropdown').removeClass('open');
}
});
// not entirely necessary. Not sure which is faster: this or just checking the width in all three places.
$(window).on('resize', function(){
$(window).data('wide', $(window).width() > 992);
// reset the open menues
$('.dropdown').removeClass('open');
$('.dropdown-menu').css({
display: '',
left: '',
position: '',
});
// because we are checking the width of the window, this should probably go here although this really should be a media query style
$('.dropdown-menu.pull-center').each(function() {
var menuW = $(this).outerWidth();
if ($(window).width() > 1000) {
$(this).css({
left: - menuW / 2 + 60 + 'px',
position: 'absolute'
});
} else {
$(this).css({
left: '',
position: ''
});
}
});
}).trigger('resize');
Initial Solution
Your question is twofold. First, you need it to not show the menu at smaller sizes. For that, you check on resize what the window width is. The problem is that it only works once. It triggers the event listeners for hover and it doesn't kill those event listeners if the screen is then larger at some point. For that, you can set a flag. There are a lot of ways to do this, but for my answer, I've chosen to use jQuery .data() method.
$(window).on('resize', function(event){
var windowSizeWide = $(window).width() > 600; // reduced for testing purposes
jQuery('ul.nav li.dropdown').data('dropdown-enabled', windowSizeWide);
}).trigger('resize');
Then when we listen for the hover events (which are mouseenter and mouseleave events), we simply return out of the function if the screen is too small.
jQuery('ul.nav li.dropdown').on('mouseenter', function() {
if(!jQuery(this).data('dropdown-enabled')) return;
jQuery(this).find('.dropdown-menu').stop(true, true).delay(200).fadeIn();
}).on('mouseleave', function() {
if(!jQuery(this).data('dropdown-enabled')) return;
jQuery(this).find('.dropdown-menu').stop(true, true).delay(200).fadeOut();
}).find('.dropdown-menu').hide();
Finally, you also want the event to trigger on load. You can do that by simply adding .trigger('resize') as seen in the first snippet. You can see a functioning demo here: http://jsfiddle.net/jmarikle/xw9Ljshu/
Possible Alternative Solution
Alternatively, you can also use CSS to handle this with media queries. The simplest way to do this is to force display: none on smaller screens. I don't recommend completely hiding the element because it becomes inaccessible at that point, but this is the general idea:
#media(max-width: 600px) {
ul.dropdown-menu {
display:none !important;
}
}
Note that !important is used because jQuery adds inline styles when you fadeIn or fadeOut.
Second demo: http://jsfiddle.net/jmarikle/xw9Ljshu/1
window.screen.availWidth to get the window size. i am yet not tested your code.But i think this will ok.
function dropdownHover() {
jQuery('ul.nav li.dropdown').hover(function() {
jQuery(this).find('.dropdown-menu').stop(true, true).delay(200).fadeIn();
}, function() {
jQuery(this).find('.dropdown-menu').stop(true, true).delay(200).fadeOut();
});
}
$(document).ready(function(){
$(window).on('resize', function(event){
var windowSize = window.screen.availWidth;
if(windowSize > 992){
dropdownHover();
}
});
})
I am using fullpage.js to create a wp site, I wonder if it is possible change the scroll direction when the user gets to a certain page.
To explain better, let's use this this example:
http://alvarotrigo.com/fullPage/examples/navigationV.html
Is it possible for the scroll direction to change to horizontal when you visit the second slide?
So instead of clicking on the arrows to navigate horizontally, I want the mouse wheel to scroll horizontally.
Is this possible with fullpage.js or do I have to change to another script?
Okay, here's the basic method:
When you get to a page that needs horizontally scrolled, add a mousewheel listener that:
Turns scroll events into left or right slide changes and
Prevents the default for the mousewheel unless:
Last slide and scroll down or
First slide and scroll up
Turn off the listener when you enter another slide.
There is also some code to prevent things from happening while slides are loading.
var currentSlide = 0;
var loaded = false;
$('#fullpage').fullpage({
navigation: true,
navigationTooltips: ['First section', 'Second section', 'Third section'],
sectionsColor: ['#f1c40f', '#e67e22', '#c0392b'],
loopHorizontal: false,
afterLoad: function (anchorLink, index){
loaded = true;
},
afterSlideLoad: function(anchorLink, index, slideAnchor, slideIndex){
currentSlide = slideIndex;
loaded = true;
},
onLeave: function(index, nextIndex, direction) {
loaded = false;
if (nextIndex == 2) {
$('#fullpage').on("mousewheel",function(e){
if(loaded){
loaded = false;
var delta = e.originalEvent.deltaY;
console.log(delta);
if(delta>0){ //down
if(currentSlide===2){//if last slide
$('#fullpage').off("mousewheel");
}else{
$.fn.fullpage.moveSlideRight();
e.preventDefault();
e.stopPropagation();
}
}else{ //up
if(currentSlide===0){//if first slide
$('#fullpage').off("mousewheel");
}else{
$.fn.fullpage.moveSlideLeft();
e.preventDefault();
e.stopPropagation();
}
}
}else{ //slide still loading, don't scroll
e.preventDefault();
e.stopPropagation();
}
});
}
}
});
JSFiddle
This works on Chrome. I'm not sure how cross browser the e.originalEvent.deltaY bit is. You can replace the mousewheel handler with this plugin to make it properly cross platform.
Here's a JSFiddle with jquery.mousewheel for a fully cross platform solution.
I have a list of links in a div with overflow. What I want to happen is that the user can navigate in this menu of links with an up and down button. I want the div to scroll up or down by the height of 1 link element every time the user clicks the corresponding button. I tried out some code but I can't seem to figure out how to make it scroll the right amount in both directions. Can anyone help me out?
All the links have the same class.
Edit:
I have already managed to scroll up and down. Now I just need to scroll in little steps of the height of 1 link.
$(function() {
var ele = $('#scroller');
var speed = 10, scroll = 5, scrolling;
$('.scroller-btn-up').click(function() {
// Scroll the element up
scrolling = window.setInterval(function() {
ele.scrollTop( ele.scrollTop() - scroll );
}, speed);
});
$('.scroller-btn-down').click(function() {
// Scroll the element down
scrolling = window.setInterval(function() {
ele.scrollTop( ele.scrollTop() + scroll );
}, speed);
});
$('.scroller-btn-up, .scroller-btn-down').bind({
click: function(e) {
// Prevent the default click action
e.preventDefault();
},
mouseleave: function() {
if (scrolling) {
window.clearInterval(scrolling);
scrolling = false;
}
}
});
});
That should be easy enough using your current code, all you have to do is get rid of the interval to stop it from scrolling repeatedly. Then you won't need the mouseleave function either, you can set the scroll variable to the same value of the height of a link tag e.g. 20 for a 20px high link tag:
$(function() {
var ele = $('#scroller');
var scroll = 20;
$('.scroller-btn-up').click(function() {
// Scroll the element up
ele.scrollTop(ele.scrollTop() - scroll);
});
$('.scroller-btn-down').click(function() {
// Scroll the element down
ele.scrollTop(ele.scrollTop() + scroll);
});
$('.scroller-btn-up, .scroller-btn-down').bind({
click: function(e) {
// Prevent the default click action
e.preventDefault();
}
});
});