Custom scrollbars – cross-browser solution

Custom scrollbars – cross-browser solution

41 292490
Custom scrollbars using javascript

Custom scrollbars – cross-browser solution

Today we will create custom stylized scrollbars for our website(s). We will use pure javascript to build own scrollbar. So this will really cross browser solution. We can use keys Up/Down keyboard keys to scroll content, mouse roller, drag and drop of scroller, etc (all what should have normal scrollbar).

In beginning – download our package and check demo:

Live Demo

[sociallocker]

download in package

[/sociallocker]


Lets start coding !


Step 1. HTML

As usual, we start with the HTML. This is source code of our sample:

index.html

<html>
<head>
    <link rel="stylesheet" href="css/main.css" type="text/css" />
    <script src="js/main.js"></script>
</head>
<body>
    <div class="example">
        <div id="main_content" style="height:90%;">
            <div class="parent">
            <h2>Arkady and Boris Strugatsky. Poor cruel folk</h2>
                <img src="images/engl_amal02.gif" alt="" />
                <div>The King sat naked. Like a foolish pauper on the street, he sat leaning
                against  a  cold  wall, drawing in his blue, goose-bumped legs. He shivered,
                with his eyes closed, he listened, but everything was quiet.</div>
                <div>He awoke at midnight from a nightmare and immediatelly understood  that
                he  was  finished.  Some  one  weezed and writhed by the door of the bedroom
                suite, he heard footsteps, metalic jingling and  drunken  mummbling  of  His
                Highness,  Uncle  Buht: "Let me through... Let me.. Break it down, hell with
                it..." Wet with icy sweat, he slintly rolled off  his  bed,  ducked  into  a
                secter  closet,  and  loosing  himself  he ran down the underground passage.
                Something sqelched under his bare feet, the startled rats dashed  away,  but
                he  did  not notice anything, just now, sitting next to a wall he remembered
                everything; the darkness, the slippery walls, and the pain from  a  blow  on
                the  head against the shakled door to the temple, and his own unberable high
                yelp.</div>
                <div>They shall not enter here, he thought. No one shall enter here. Only if
                the King  order's  so.  But  the  King  shall  not  order...  He   snickered
                hysterically. Oh no, the King will not order! He carefully un screwed up his
                eyes  and  saw  his  blue, hairless legs with scraped knees. Still alive, he
                thought. I will live, because they shall not enter here.</div>
                <div>Everything in the temple  was  blueish  from  the  cold  light  of  the
                lanterns -- long glowing tubes that were stretched under the ceiling. In the
                center,  God stood on an eminence, big, heavy, with sparkling dead eyes. The
                King continuously and stupidly stared, until God was suddenly screened by  a
                shabby  lay  brother,  still  a  greenhorn. Scraching, with an open mouth he
                gazed at the naked King. The King squinted once again. Scum, he  thought,  a
                lousy  vermine,  catch the mongrel and to the dogs, for them to ravage... He
                reasoned that he did not remember the lout well, but he was  long  gone.  So
                scrawny,   snotty...   That's  all  right,  we'll  remember.  We'll  remeber
                everything, Your Highness, Uncle Buht. During the father's  reighn,  I  dare
                say you sat quietly, drank a bit and kept silent, were afraid to be noticed,
                you knew that King Prostyaga did not forget you ignoble treachery...</div>
                <div>Great  was  the father, the King thought with an accustomed envy. You'd
                be great, if your advisors are God's angels in flesh.  All  know,  all  have
                seen  them:  their  faces  fearful, white, like milk, and their garment were
                such that one could not understand if they were  naked  or  not.  And  their
                arrows  were  fiery,  like  lightning,  they  drove  off the nomads with the
                arrows, and although they casted them overhead, half the horde cripled  from
                fear.  His  Highness,  Uncle  Buht,  wispered  once  upon  a time, drunk and
                burping, that those arrows can be cast by anyone, that  special  slings  are
                needed that the angels have and that would be nice to take from them. And he
                said  then -- he was drunk then, -- that if it is nice to have, why not have
                it, why not... Soon after that table talk one angel fell off the  wall  into
                the  moat,  probably  slipped.  Next  to  him they found one of uncle's body
                guards with a javelin between his shoulder  blades.  It  was  a  dark,  dark
                deed...  It  good  that  the people did not care about the angels, they were
                scary to look at, but it is not clear why is it scary -- angels were  happy,
                cordial  people.  Only  their  eyes  were scary. Small, shiny, and they keep
                racing around... non humanoid eyes, not peaceful. So the people hushed down,
                although father, King Prostyaga gave them such freedom that it  is  shameful
                to  remember...  although,  before  the  Coup, father, they say was a saddle
                maker. For saying so, with my own hands I had torn eyes out, and sewen  ears
                shut.  But  I remember, he used to sit in the evenings by the Crystal Tower,
                and he would cut out leather -- beautiful work. And I would perch myself  at
                his  side, it's warm and comfy... The angels were singing from the rooms, so
                quietly, and in harmony, and father would start  to  accompany  --  he  knew
                their  language  --  it  used to be spacious, nobody around... not like now,
                guards are stuck at every corner, but there is no sense in it...</div>
                <div>The King lamented. Yes, he was a good father, just that he did not  die
                for  a long time. You can't do that while your son is still alive... The son
                is also the King, the son also want's to... But Prostyaga did not  age,  I'm
                over  fifty,and  he  still looks younger than me... It looks like the angels
                had asked God for his health... They asked for his health, but  they  forgot
                about  me.  They  say  that  the  second one they managed to pin down in the
                father's room, he had a sling in each hand, but he  did  not  fight.  Before
                death,  they  say,  he  threw both of them out the window, they burst into a
                blue flame, there was no dust  left...  Too  bad  about  the  slings...  And
                Prostyaga, they say, cried and got drunk then, within an inch of his life --
                the  first  time  since  his  reign  --  looked for me, they said, loved me,
                believed...</div>
                <div>The King drew his knees to his chin, and hugged his leggs. So  what  if
                he  believed?  One  should  know  one's  limit,  abdicate,  like  it is done
                elsewhere... and I do not know anything, and do not want to. There was  only
                a conversation with my uncle, His Highnesss.</div>
                <div>"Prostyaga, -- he said, -- doesn't age". -- "Yes, -- I tell him, -- but
                what can  we  do,  the  angels  pleaded for his health." Uncle then sneered,
                scum, and wispered: "Angels, -- he said,  --  no  longer  sing  their  songs
                here".  And  I blurted out: "It is true, but now there is a way to deal with
                them, not just with humans". Uncle looked at me  soberly,  and  immediatelly
                left...  And I didn't really say anything... Empty words, without meaning...
                And in a week Prostyaga died from a heart attack. So what? It was his  time.
                He looked young, but in reality he was over one hundred. We'll all die...</div>
                <div>The King was startled, and covering himself, awkwardly sat up. Into the
                temple  came  the  High  Priest  Agar.  Lay brothers were leading him by the
                hands. He didn't look at the King, came up to God and kneeled  in  front  of
                the  eminence,  tall,  hunch-backed, with waist length dirty-white hair. The
                King gloated "It's the end of you, Your Highness, you did  manage,  I'm  not
                like  Prostyaga,  you'll  ravage your oun intestines, drunken swine..." Agar
                spoke in a rich voice:</div>
                <div>- God! The King wishes to speak to you! Forgive him and listen!</div>
                <div>The room fell silent, no-one dared to breathe. The  King  contemplated:
                when  the  great flood happened, and the earth burst, Prostyaga asked God to
                help, and God came down from the sky as a ball of flame on the same day, and
                that night the earth closed up, and the flood  disappeared.  It  means  that
                this  is  how  it will happen today. You were late uncle, Your Highness, you
                didn't manage. No one can help you now...</div>
                <div>Agar straighned up. The lay brothers that supported him,  jumped  away,
                turned with their backs to God, and covered their heads with their arms. The
                Kind  saw,  how  Agar stretced his clasped hands and put them on Gods chest.
                God's eyes lit up. The King snapped his jaw from fear: the eyes were big and
                different -- one was snakish-green, the other white, as bright as light. One
                could hear how  God  started  to  breathe,  heavily,  with  crackling,  like
                consumption. Agar backed away.</div>
                <div>- Speak, - he whispered. It looked like he was unsettled as well.</div>
                <div>The King lowered to all fours, and started to crawl to the eminence. He
                did not  know what to do or how. And he did not know how he should start and
                whether he should tell the complete truth. God  breathed  heavily,  weezing,
                suddenly he started to whimper, quietly and thinly - scary.</div>
                <div>- I'm  the son of Prostyaga -- said the King in despair, smothering his
                face against the cold stone. -- Prostyaga died. I ask  protection  from  the
                conspirators.  Prostyaga made mistakes. He did not know what he was doing. I
                have fixed everything: calmed the people, became great and unatainable, like
                you, I gathered an army... And the treacherous Buht is disrupting  my  plans
                to conquer the world... He wants to kill me! Help me!</div>
                <div>He raised his head. God, without blinking, was looking in his face with
                green and white. God was silent.</div>
                <div>- Help me... - repeated the King. -- Help! Help! - He suddenly thought,
                that he  is  doing something wrong, and that God is indifferent towards him,
                and inopportunely remembered: they said, his father, Prostyaga, did not  die
                from  a  heart  attack,  but was killed here, in the temple when the killers
                came in, with out asking permission. -- Help!.-- he  screemed  desperatelly.
                -- I'm afraid to die today! Help! Help!</div>
                <div>He  hunched  up  on the stone tiles, biting his hands from an unbarable
                terror. Differently-eyed God hoarsly breathed above his head.</div>
                <div>- Old vermine, - said Tolya. Ernst was quiet. On  the  screen,  through
                the  sparks  of  static an ugly black shape of a human lay splattered on the
                floor. -- When I think, Tolya spoke again, -- that if not for him, Alan  and
                Derek would be alive, I want to do something, that you never wanted to do.</div>
                <div>Ernst shrugged his shoulders and moved to the table.</div>
                <div>- And  I  always think, - Tolya continued, - why didn't Derek shoot? He
                could have killed all...</div>
                <div>- He couldn't , - said Ernst.</div>
                <div>- Why couldn't he?</div>
                <div>- Have you ever tried shooting at a human being?</div>
                <div>Tolya made a wry face, but didn't say anything.</div>
                <div>- Well that's what it was, - said Ernst. -- Try to imagine  it.  It  is
                almost as disgusting.</div>
                <div>A  sorowful howl was heard from the loudspeaker. "HELP HELP I AM AFRAID
                HELP..," the auto-translater was writing.</div>
                <div>- Poor cruel folk... - said Tolya.</div>
            </div>
        </div>
    </div>
</body>
</html>

This is sample of content (from one book) – it have pretty much text, right? Soon (at third step) we will apply pagination to our ‘main_content’.

Step 2. CSS

Here are used CSS file with styles of our demo:

css/main.css

body {
background:#eee;
margin:0;
padding:0;
}
.example {
-moz-border-radius:3px;
-webkit-border-radius:3px;
background:#FFF;
border:1px #000 solid;
height:600px;
margin:20px auto;
padding:15px;
position:relative;
width:800px;
}
#main_content {
height:50%;
left:5%;
overflow:auto;
position:absolute;
top:5%;
width:90%;
}
.ssb_down {
background:transparent url(../images/icon-arrow-down.png);
bottom:0;
cursor:pointer;
position:absolute;
right:0;
}
.ssb_sb {
background:transparent url(../images/middle.png);
cursor:pointer;
position:absolute;
right:0;
}
.ssb_sb_down {
background:transparent url(../images/middrag.png);
}
.ssb_sb_over {
background:transparent url(../images/midhover.png);
}
.ssb_st {
background:transparent url(../images/back.png);
cursor:pointer;
height:100%;
position:absolute;
right:0;
top:0;
}
.ssb_up {
background:transparent url(../images/icon-arrow-up.png);
cursor:pointer;
position:absolute;
right:0;
top:0;
}
.parent {
font-family:verdana;
height:100%;
padding:10px;
position:relative;
}

Step 3. JS

Here are all JS files:

js/main.js

var ssb = {
    aConts  : [],
    mouseY : 0,
    N  : 0,
    asd : 0, /*active scrollbar element*/
    sc : 0,
    sp : 0,
    to : 0,
    // constructor
    scrollbar : function (cont_id) {
        var cont = document.getElementById(cont_id);
        // perform initialization
        if (! ssb.init()) return false;
        var cont_clone = cont.cloneNode(false);
        cont_clone.style.overflow = "hidden";
        cont.parentNode.appendChild(cont_clone);
        cont_clone.appendChild(cont);
        cont.style.position = 'absolute';
        cont.style.left = cont.style.top = '0px';
        cont.style.width = cont.style.height = '100%';
        // adding new container into array
        ssb.aConts[ssb.N++] = cont;
        cont.sg = false;
        //creating scrollbar child elements
        cont.st = this.create_div('ssb_st', cont, cont_clone);
        cont.sb = this.create_div('ssb_sb', cont, cont_clone);
        cont.su = this.create_div('ssb_up', cont, cont_clone);
        cont.sd = this.create_div('ssb_down', cont, cont_clone);
        // on mouse down processing
        cont.sb.onmousedown = function (e) {
            if (! this.cont.sg) {
                if (! e) e = window.event;
                ssb.asd = this.cont;
                this.cont.yZ = e.screenY;
                this.cont.sZ = cont.scrollTop;
                this.cont.sg = true;
                // new class name
                this.className = 'ssb_sb ssb_sb_down';
            }
            return false;
        }
        // on mouse down on free track area - move our scroll element too
        cont.st.onmousedown = function (e) {
            if (! e) e = window.event;
            ssb.asd = this.cont;
            ssb.mouseY = e.clientY + document.body.scrollTop + document.documentElement.scrollTop;
            for (var o = this.cont, y = 0; o != null; o = o.offsetParent) y += o.offsetTop;
            this.cont.scrollTop = (ssb.mouseY - y - (this.cont.ratio * this.cont.offsetHeight / 2) - this.cont.sw) / this.cont.ratio;
            this.cont.sb.onmousedown(e);
        }
        // onmousedown events
        cont.su.onmousedown = cont.su.ondblclick = function (e) { ssb.mousedown(this, -1); return false; }
        cont.sd.onmousedown = cont.sd.ondblclick = function (e) { ssb.mousedown(this,  1); return false; }
        //onmouseout events
        cont.su.onmouseout = cont.su.onmouseup = ssb.clear;
        cont.sd.onmouseout = cont.sd.onmouseup = ssb.clear;
        // on mouse over - apply custom class name: ssb_sb_over
        cont.sb.onmouseover = function (e) {
            if (! this.cont.sg) this.className = 'ssb_sb ssb_sb_over';
            return false;
        }
        // on mouse out - revert back our usual class name 'ssb_sb'
        cont.sb.onmouseout  = function (e) {
            if (! this.cont.sg) this.className = 'ssb_sb';
            return false;
        }
        // onscroll - change positions of scroll element
        cont.ssb_onscroll = function () {
            this.ratio = (this.offsetHeight - 2 * this.sw) / this.scrollHeight;
            this.sb.style.top = Math.floor(this.sw + this.scrollTop * this.ratio) + 'px';
        }
        // scrollbar width
        cont.sw = 20;
        // start scrolling
        cont.ssb_onscroll();
        ssb.refresh();
        // binding own onscroll event
        cont.onscroll = cont.ssb_onscroll;
        return cont;
    },
    // initialization
    init : function () {
        if (window.oper || (! window.addEventListener && ! window.attachEvent)) { return false; }
        // temp inner function for event registration
        function addEvent (o, e, f) {
            if (window.addEventListener) { o.addEventListener(e, f, false); ssb.w3c = true; return true; }
            if (window.attachEvent) return o.attachEvent('on' + e, f);
            return false;
        }
        // binding events
        addEvent(window.document, 'mousemove', ssb.onmousemove);
        addEvent(window.document, 'mouseup', ssb.onmouseup);
        addEvent(window, 'resize', ssb.refresh);
        return true;
    },
    // create and append div finc
    create_div : function(c, cont, cont_clone) {
        var o = document.createElement('div');
        o.cont = cont;
        o.className = c;
        cont_clone.appendChild(o);
        return o;
    },
    // do clear of controls
    clear : function () {
        clearTimeout(ssb.to);
        ssb.sc = 0;
        return false;
    },
    // refresh scrollbar
    refresh : function () {
        for (var i = 0, N = ssb.N; i < N; i++) {
            var o = ssb.aConts[i];
            o.ssb_onscroll();
            o.sb.style.width = o.st.style.width = o.su.style.width = o.su.style.height = o.sd.style.width = o.sd.style.height = o.sw + 'px';
            o.sb.style.height = Math.ceil(Math.max(o.sw * .5, o.ratio * o.offsetHeight) + 1) + 'px';
        }
    },
    // arrow scrolling
    arrow_scroll : function () {
        if (ssb.sc != 0) {
            ssb.asd.scrollTop += 6 * ssb.sc / ssb.asd.ratio;
            ssb.to = setTimeout(ssb.arrow_scroll, ssb.sp);
            ssb.sp = 32;
        }
    },
    /* event binded functions : */
    // scroll on mouse down
    mousedown : function (o, s) {
        if (ssb.sc == 0) {
            // new class name
            o.cont.sb.className = 'ssb_sb ssb_sb_down';
            ssb.asd = o.cont;
            ssb.sc = s;
            ssb.sp = 400;
            ssb.arrow_scroll();
        }
    },
    // on mouseMove binded event
    onmousemove : function(e) {
        if (! e) e = window.event;
        // get vertical mouse position
        ssb.mouseY = e.screenY;
        if (ssb.asd.sg) ssb.asd.scrollTop = ssb.asd.sZ + (ssb.mouseY - ssb.asd.yZ) / ssb.asd.ratio;
    },
    // on mouseUp binded event
    onmouseup : function (e) {
        if (! e) e = window.event;
        var tg = (e.target) ? e.target : e.srcElement;
        if (ssb.asd && document.releaseCapture) ssb.asd.releaseCapture();
        // new class name
        if (ssb.asd) ssb.asd.sb.className = (tg.className.indexOf('scrollbar') > 0) ? 'ssb_sb ssb_sb_over' : 'ssb_sb';
        document.onselectstart = '';
        ssb.clear();
        ssb.asd.sg = false;
    }
}
window.onload = function() {
    ssb.scrollbar('main_content'); // scrollbar initialization
}

Binded events, other scrolling functionality – all here. I tried to make comments in many important places.


Live Demo

Conclusion

Today`s article told you about creating nice customized scrollbar for content. Sure that this was useful for you. Your comments are welcome. Good luck!

SIMILAR ARTICLES

Understanding Closures

0 24610

41 COMMENTS

  1. Hello,

    Will this work with a class name to initiate the script instead of an id name? I want to use it to scroll a lot of different divs with the same classname within the same page.

    • Hello S3nd41,

      Please pay attention to line 12 in our JS file:
      var cont = document.getElementById(cont_id);
      So, I am taking necessary element by its ID.
      If you like to use class name instead id – you have to change this line (to get element by its class name)

  2. Hi, your tutorial is wonderful. I have one question though .. Can we use overflow:auto, i mean I dont want the scrollbar when the content is not scrollable height. with this scrollbar the scrollbar appears even if it is not needed.

    • Hello Ibrahim,

      overflow:auto is always applied to ‘main_content’ element. In your case (to hide scrollbar if not necessary) I can suggest you modify code a little (in constructor). Need to calculate total height, and, compare with height of parent, and, if need – do ‘return’ here to hide scrollbar.

      • after the line: if (! ssb.init()) return false;
        just add another line like this: if(cont.scrollHeight <= cont.clientHeight) return false;
        and you're good to go on auto-hiding the scrollbar for non-oerflowing content.

        Note to the author: your clone also copies the id attribute and now you have 2 elements with the same id. Not ideal.

    • Hi Abel,

      Right now – this is only vertical scrollbar, not horizontal, but you can try understand logic and develop own horizontal scrollbal

  3. ahoi, nice script.

    i want to use it on different divs on the same page,
    is it possible to change this to use an array of IDs?

  4. Great!!!
    I want only horizontal scrollbar, and I’m trying to wrote this below code: but not working
    Hope you kindly to show me how to do with CSS and JS

    #main_content {
    width:700px;
    height:200px;
    left:5%;
    overflow-x:scroll;
    overflow-y:hidden;
    position:absolute;
    top:5%;

    Thanks

    • Hello Lee,
      It will not only CSS changes in your case. I made vertical scrollbar. Check it realization. It have own event handling functions (javascript), not only styles. If you need to have horizontal scrollbar, we have to update JS code too of course.

  5. Hi! thank for the trick, sorry for my english but i wanna know how add another ID’s in the template, only work for #main_content but i dont know how add anothers ID’s…

    Thank You!
    Colombia say HI!!

  6. Its me again, sorry i forget, i have the same question Maverik, how add several initializations? :P

    Yes… i know in the .JS but in the line 184 but i do not know how…

    Please Help Us!!
    Thanks
    God Bless You

    • Hi Camilo Rosero,
      Honestly, I think that it will be better to clone whole plugin, (you can use the same main.js file too).
      Because you will need to draw new horizontal bar and add own mouse handlers to it.

  7. Hello! I wanted to make the slider more narrow than original (20px in js – line #89). But when I use lower value (5px for example) the default slider is visible behind the custom one. Do you have any solution for that?
    Thanks!

    • Hello ljubo,
      Yes, you can also apply ‘overflow: hidden’ for #main_content element (instead ‘auto’)

    • Hello avisek,
      Yes, sure. In the constructor we clone our initial parent object with content, then we add custom styles for our new object. Then, we add 4 custom DIVs for our object (scrollbar elements). After, we add custom onmousedown/onmouseout/onmouseover for scrollbar elements (to shift position of our main area).

  8. Iv tried a few scrollbars, all based on jquery or scriptaculous, and although they all had merit, there were SOOO many conflicts with my existing jquery, id almost given up on using a custom scrollbar. This one is AWESOME!!
    Thanks so much for this kick ass scrollbar!!

  9. Excellent implementation. Based on your structures we can also implement Horizontal scroll by modifying some CSS and math calculations etc all in the same Object. Thanks.

  10. Hi,

    Great tutorial! Its a simple and useful one. Thank you..
    And I’m new to Js.. I’ve one doubt, some mootools not accept this js file because of the last 3 lines window onload function..
    Any other solution without the onload funtion.

    Thanks for Advance!

    • Hello Shamee,
      I’m pretty sure that mootools should have own realization of ‘windows-onload’. Just search in official documentation of mootools.

  11. Doing this hides the default, but also makes it not scroll for me…

    Please help!

    Thank-you!

  12. Hi!

    Wonderful tutorial! Especially for beginners like me. I wonder how can I place the scrollbar to the left instead having it to the right. Thanks again :)

  13. Hi Andrew,

    This article is very great article for me. Could you please suggest me on following: Actually, I am using it for a popup. In the popup, initially scroll handle is missing. Kindly suggest me to do the required code.

  14. Greetings,
    First of all, this tutorial has been one of the best that I have found that works for all browsers. (Not to mention one of the smallest). My question is that I would like to not have the upper and lower buttons, and have the handlebar go all the way to the top and bottom respectfully.
    When I hide the images using CSS methods or going into the code and changing the height of the items to 0, it doesn’t change where the handlebar stops.

    • Hi Jordan,
      After you removed (hided) both scroll elements, pay attention to the this.ratio value on line 84

  15. Same comment as Wolf. I have tried many other scrollbars that would not work with my jQuery. The element I am applying the scrollbar to is initially hidden, and i am using the slideUp() and slideDown() methods. Your scrollbar works great with these where others failed on certain browsers. I am using it on my test site at the moment but intend to use it on my main site soon. Thank you.

  16. Did anyone try it on OSX Lion?
    I got this problem with the default scrollbar still showing:
    http://imgur.com/PipzHPG

    Let me know if you have found any solution!
    Cheers

  17. Hi. Just did a quick check on this issue and found many non working solutions ;)

    So thank you very much! Great job, Especially when you look on when it was done. NICE! and there seems to be plenty more. Wonderfull!

    Actually i’m not even able to use it in this case. but still – saved it for later :)

    Thankfully yours,
    Stefan

  18. Hello,
    I am trying to use this on the DropDownList control of ASP.net. I want the scroll Bar of the control to be customized. I have added all the files corrected but still cant get the required result.
    Will this .js work for the DropDownList control.
    Any help would be appreciated.

Leave a Reply