ScrollSpy in pure javascript

Door johnydoe op vrijdag 13 april 2012 23:11 - Reacties (6)
Categorie: javascript, Views: 6.309

De laatste tijd zie je steeds vaker de ScrollSpy plugin voor jQuery langskomen. Deze plugin houdt in de gaten waar een bezoeker op dit moment naar kijkt en kan laten weten of een bepaald DOM element binnen de viewport van de browser valt. Deze plugin is ook beschikbaar voor backbone.js en mootools en mede populair gemaakt door Twitter Bootstrap en Jira.

De subnavigatiebalk staat gewoon op een relatieve positie op de pagina en scrollt mee
Twitter bootstrap 1

De subnavigatiebalk hangt bovenaan de pagina
Twitter bootstrap 2

Nou wilde we bij Tweakers.net ook een soortgelijke functionaliteit gaan gebruiken, maar dan zonder op deze frameworks te leunen. Hiervoor heb ik in een kleine 96 regels een pure javascript implementatie gemaakt, die overal als drop-in gebruikt kan worden (ook binnen andere frameworks), werkt met custom events en zelfs in Internet Explorer (heb tot 7 getest) en touch devices (getest op iOS) werkt.

De volledige implementatie is te vinden in een Gist, maar de ScrollSpy klasse heeft de volgende methodes:
- init()
- spyOn(DOMElement)

De init() methode wordt gebruikt om daadwerkelijk naar scroll en touch events te luisteren. Het zou kunnen dat je dat niet direct na het aanmaken van een instantie wil doen, vandaar dat dit los getrokken is.

Met de spyOn(DOMElement), kun je een DOM element aangeven wat in de gaten gehouden moet worden.

Om bijvoorbeeld een header element fixed aan de bovenkant van de pagina te houden wanneer er naar beneden gescrollt wordt (zoals in Twitter Bootstrap), kun je de volgende code gebruiken:

code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
var StickyHeader = (function()
{
        function init()
        {
                var scrollSpy = new ScrollSpy();
                scrollSpy.init();

                var header = document.getElementById('header');
                scrollSpy.spyOn(header);

                if (document.addEventListener)
                {
                        document.addEventListener('ScrollSpyOutOfSight', setSticky, false);
                        document.addEventListener('ScrollSpyBackInSight', setLoose, false);
                }
                else if (document.attachEvent)
                {
                        document.attachEvent('onpropertychange', handleIeEvent)
                }
        }

        function handleIeEvent(event)
        {
                if (event.expando == 'ScrollSpyOutOfSight')
                        setSticky(event);
                if (event.expando == 'ScrollSpyBackInSight')
                        setLoose(event);
        }

        function setSticky(event)
        {
                var header = event.data;
                var body = document.getElementById('body');

                header.className = 'header fixed';
                body.className = 'body fixed';
        }

        function setLoose(event)
        {
                var header = event.data;
                var body = document.getElementById('body');

                header.className = 'header';
                body.className = 'body';
        }

        return {
            init: init
        };
});

var stickyHeader = new StickyHeader();
stickyHeader.init();



Ik zal je niet lastig gaan vallen met de css regels die bij de header, body en fixed klasses horen, tenzij er echt vraag naar is.

Klein onderschrift: een event wordt alleen maar getriggerd wanneer een object de viewport aan de bovenkant verlaat. Ik laat het als oefening aan de gebruiker over om dit naar eigen inzicht aan te passen, dat zou relatief triviaal moeten zijn.

Mocht je aanpassingen hebben die het geheel beter kunnen maken, of kleiner, of sneller, schroom dan niet om te reageren!

Reacties


Door Tweakers user roeleboel, vrijdag 13 april 2012 23:58

onder welke licentie breng je dit uit? (gpl/lgpl/bsd/joost-mag-het-weten/...)

[Reactie gewijzigd op vrijdag 13 april 2012 23:58]


Door Tweakers user Xuj, zaterdag 14 april 2012 13:14

Het lijkt me niet verstandig om element.className = 'class' te gebruiken; namen van klassen toevoegen lijkt me een beter plan.

Voor je het weet vervang je klassen die op het element staan, welke erop gezet zijn door externe javascript libraries.

Verder wel een net light-weight dingetje.

Door Tweakers user HendrikN, zaterdag 14 april 2012 22:09

Met Backbone.js bedoel je vast (Twitter) Bootstrap? ;)

Door Tweakers user YopY, zondag 15 april 2012 00:27

HendrikN schreef op zaterdag 14 april 2012 @ 22:09:
Met Backbone.js bedoel je vast (Twitter) Bootstrap? ;)
nein ;)

Door Tweakers user prutsger, maandag 16 april 2012 10:51

Om bijvoorbeeld een header element fixed aan de bovenkant van de pagina te houden wanneer er naar beneden gescrollt wordt (zoals in Twitter Bootstrap), kun je de volgende code gebruiken
Sinds wanneer doen we dit meer met pure css? Sneller verwerkt, minder geheugen.

Door Tweakers user johnydoe, maandag 16 april 2012 11:44

prutsger schreef op maandag 16 april 2012 @ 10:51:
[...]

Sinds wanneer doen we dit meer met pure css? Sneller verwerkt, minder geheugen.
Zoals je kunt zien wordt het daadwerkelijk fixed houden ook gedaan met css, uiteraard! Maar het vervelende is dat de classes verwisseld moeten worden gebaseerd op waar het element in de huidige viewport staat. Dat is vziw niet goed mogelijk met alleen css.
Xuj schreef op zaterdag 14 april 2012 @ 13:14:
Het lijkt me niet verstandig om element.className = 'class' te gebruiken; namen van klassen toevoegen lijkt me een beter plan.

Voor je het weet vervang je klassen die op het element staan, welke erop gezet zijn door externe javascript libraries.

Verder wel een net light-weight dingetje.
Zeker, de voorbeeld code die ik aangaf is ook alleen dat, voorbeeld code. Binnen Tweakers.net gebruiken we nog een custom library voor het werken met css classes, waardoor dit in het geheel anders gaat. Ik laat het dan ook aan de lezer over om het voorbeeld naar eigen inzicht in te zetten.
roeleboel schreef op vrijdag 13 april 2012 @ 23:58:
onder welke licentie breng je dit uit? (gpl/lgpl/bsd/joost-mag-het-weten/...)
Ik heb de gist aangepast zodat er een duidelijke LGPL licentie boven staat.

Reageren is niet meer mogelijk