JS Code Of The Week: DoBlockScroll();
Have you ever had a frustrating mouse wheel experience where you scrolled a <div>-container, but when you reached the end you accidentally scrolled the whole page? Well, I certainly have had my "fun" with this annoyance, for a very long time now. But today I stand tall and I proudly say "NAY!" as I present my JavaScript Code Snippet Of The Week called "DoBlockScroll()". [...]
(show me)(don't show me)
Now, when I say "DoBlockScroll" it is meant to be a question, as in "do block scroll or do not block scroll?". I always do that with my boolean returning functions, I give them "DoX" names. And it is a boolean returning function that I'm presenting you because that is the hardest part: finding out when to do it. I thought I would also need a How To for the blocking part, but no, the blocking itself roots down to a simple return false; as you may see further down below. I even made the function as general as possible so you could probably just copy'n'paste it to use for your own stuff.
As long as you're an Opera user. I didn't test it with FireFox or IE and I know that it should not work for them the way they are now. Alone the event call is handled differently in FireFox as far as I know. But frankly I don't give a shit about javascripting for FireFox or IE. I guess though, that it's probably simple enough to alter the code for other browsers. I'll let someone else have the fun.
The setup I used this for is a table within a div-container. The table features lots of data while the purpose of the div-container is to present this data neatly dosed. Something like this:
<div style='overflow: auto; height: 400px;'><table id='TableX'></table></div>
The objects don't have to be tables and divs, they just need to have this auto-overflow-attribute and the id - I guess they even don't need to be two separate objects. My assumption is that if you alter the upcoming variable IsLowerEnd even that adventure should indeed prove fruitful. Needless excursions brused aside, next comes the call of DoBlockScroll() which either looks like the first version if you implement onmouse* events dynamically (for whatever reason) or like the second version if you decide to directly write it all down.
(1) document.all.TableX.onmousewheel = function(){ return !DoBlockScroll( this, event ); }
(2) <table id='TableX' width=100% onmousewheel='return !DoBlockScroll( this, event );'></table>
Don't forget to give the table a width=100% attribute so that it spans over the entire width of the div-container. Otherwise you may wonder why the scrolling won't be regonized. Anyway, this (1) is what an onmouse* event looks like when you define it dynamically. Onmousewheel creates a mouse-related variable called event as all onmouse* functions do. Just in case you were wondering were the hell that came from. With this however you may google it yourself if you don't know about it.
[ Woody's Checkpoint: # top # ]
The basic idea behind DoBlockScroll() is to check three conditions that result in the wish to block the scrolling: is the user scrolling up although the scrollbar is already at its top? Or does this occur for the lower end of the scrollbar? And is the mouse over the object altogether? As this description already implies the code handles only a vertical scroll situation. Naturally, horizontal is possible, too.
/*
determines whether to block a scroll event or not; returns boolean
PARAM.: Tab is the table within the div object which has an overflow attribute
the objects don't have to be tables and divs.
PARAM.: event is the bequeathed onmousewheel event
*/
function DoBlockScroll( Tab, event )
{
// first search Div
var Div = Tab, Bool = false; // Bool returns false as default;
// don't search too far && it's not the one yet
while ( ( Div.nodeName.toLowerCase() != "body" ) && ( Div.style.overflow != "auto" ) )
Div = Div.parentNode;
// if found
if ( event && ( Div.nodeName.toLowerCase() != "body" ) )
{
var Cond1 = false, Cond2 = false, Cond3 = false,
IsUpperEnd = Div.scrollTop == 0,
IsLowerEnd = Tab.offsetHeight == eval( Div.offsetHeight + Div.scrollTop );
// on scroll up when Tab is already as up as it can be
Cond1 = ( event.wheelDelta > 0 ) && IsUpperEnd;
// same for bottom
Cond2 = ( event.wheelDelta < 0 ) && IsLowerEnd;
// is mouse over object
Cond3 = IsChildOfObject( event.srcElement, Tab );
Bool = ( Cond1 || Cond2 ) && Cond3;
}
return Bool;
}
Of course, you also need IsChildOfObject(). I already had that one.
// recursively checks for a (grand)parent-child-relationship
function IsChildOfObject( Ch, Obj ) // Child element
{
if ( Ch == Obj ) // super special case: you can't be your own child
return false;
else
return IsChildOfObject_sub( Ch, Obj );
}
function IsChildOfObject_sub( Ch, Obj )
{
// termination condition
if ( Ch.tagName.toLowerCase() == "body" ) // this is by far too far
return false;
else if ( Ch == Obj ) // this is just right
return true;
else // this is 'not found yet'
return IsChildOfObject_sub( Ch.parentNode, Obj );
}
# top #
(show me)(don't show me)
Now, when I say "DoBlockScroll" it is meant to be a question, as in "do block scroll or do not block scroll?". I always do that with my boolean returning functions, I give them "DoX" names. And it is a boolean returning function that I'm presenting you because that is the hardest part: finding out when to do it. I thought I would also need a How To for the blocking part, but no, the blocking itself roots down to a simple return false; as you may see further down below. I even made the function as general as possible so you could probably just copy'n'paste it to use for your own stuff.
As long as you're an Opera user. I didn't test it with FireFox or IE and I know that it should not work for them the way they are now. Alone the event call is handled differently in FireFox as far as I know. But frankly I don't give a shit about javascripting for FireFox or IE. I guess though, that it's probably simple enough to alter the code for other browsers. I'll let someone else have the fun.
The setup I used this for is a table within a div-container. The table features lots of data while the purpose of the div-container is to present this data neatly dosed. Something like this:
<div style='overflow: auto; height: 400px;'><table id='TableX'></table></div>
The objects don't have to be tables and divs, they just need to have this auto-overflow-attribute and the id - I guess they even don't need to be two separate objects. My assumption is that if you alter the upcoming variable IsLowerEnd even that adventure should indeed prove fruitful. Needless excursions brused aside, next comes the call of DoBlockScroll() which either looks like the first version if you implement onmouse* events dynamically (for whatever reason) or like the second version if you decide to directly write it all down.
(1) document.all.TableX.onmousewheel = function(){ return !DoBlockScroll( this, event ); }
(2) <table id='TableX' width=100% onmousewheel='return !DoBlockScroll( this, event );'></table>
Don't forget to give the table a width=100% attribute so that it spans over the entire width of the div-container. Otherwise you may wonder why the scrolling won't be regonized. Anyway, this (1) is what an onmouse* event looks like when you define it dynamically. Onmousewheel creates a mouse-related variable called event as all onmouse* functions do. Just in case you were wondering were the hell that came from. With this however you may google it yourself if you don't know about it.
[ Woody's Checkpoint: # top # ]
The basic idea behind DoBlockScroll() is to check three conditions that result in the wish to block the scrolling: is the user scrolling up although the scrollbar is already at its top? Or does this occur for the lower end of the scrollbar? And is the mouse over the object altogether? As this description already implies the code handles only a vertical scroll situation. Naturally, horizontal is possible, too.
/*
determines whether to block a scroll event or not; returns boolean
PARAM.: Tab is the table within the div object which has an overflow attribute
the objects don't have to be tables and divs.
PARAM.: event is the bequeathed onmousewheel event
*/
function DoBlockScroll( Tab, event )
{
// first search Div
var Div = Tab, Bool = false; // Bool returns false as default;
// don't search too far && it's not the one yet
while ( ( Div.nodeName.toLowerCase() != "body" ) && ( Div.style.overflow != "auto" ) )
Div = Div.parentNode;
// if found
if ( event && ( Div.nodeName.toLowerCase() != "body" ) )
{
var Cond1 = false, Cond2 = false, Cond3 = false,
IsUpperEnd = Div.scrollTop == 0,
IsLowerEnd = Tab.offsetHeight == eval( Div.offsetHeight + Div.scrollTop );
// on scroll up when Tab is already as up as it can be
Cond1 = ( event.wheelDelta > 0 ) && IsUpperEnd;
// same for bottom
Cond2 = ( event.wheelDelta < 0 ) && IsLowerEnd;
// is mouse over object
Cond3 = IsChildOfObject( event.srcElement, Tab );
Bool = ( Cond1 || Cond2 ) && Cond3;
}
return Bool;
}
Of course, you also need IsChildOfObject(). I already had that one.
// recursively checks for a (grand)parent-child-relationship
function IsChildOfObject( Ch, Obj ) // Child element
{
if ( Ch == Obj ) // super special case: you can't be your own child
return false;
else
return IsChildOfObject_sub( Ch, Obj );
}
function IsChildOfObject_sub( Ch, Obj )
{
// termination condition
if ( Ch.tagName.toLowerCase() == "body" ) // this is by far too far
return false;
else if ( Ch == Obj ) // this is just right
return true;
else // this is 'not found yet'
return IsChildOfObject_sub( Ch.parentNode, Obj );
}
# top #
Labels: personal, programming
posted by Woodrow at 7/19/2009 10:46:00 PM
0 comments
0 Comments:
Post a Comment