Questo è qualcosa che la persona twitter ha dovuto dire.
jQuery è uno strumento fantastico, ma c'è mai un momento in cui non dovresti usarlo? In questo tutorial, vedremo come creare un interessante comportamento di scorrimento con jQuery, e quindi rivedere come il nostro progetto potrebbe essere migliorato rimuovendo il più possibile jQuery o astrazione.
A volte il tuo codice può essere ancora più magico? sottraendo parte di quella bontà jQuery.
Potresti aspettarti che questo sia il tuo normale tutorial do-something-awesome-with-jQuery. In realtà, non lo è. Mentre potresti finire per creare un effetto piuttosto interessante ma, francamente, forse altrettanto inutile, non è questo il punto principale che voglio che tu togli da questo tutorial.
Come puoi vedere, voglio che impari a guardare il jQuery che stai scrivendo come un normale JavaScript, e renditi conto che non c'è niente di magico in questo. A volte il tuo codice può essere ancora più magico? sottraendo parte di quella bontà jQuery. Speriamo che, alla fine, sarai un po 'più bravo a sviluppare con JavaScript rispetto a quando hai iniziato.
Se questo sembra troppo astratto, considera questa lezione una lezione di performance e di refactoring del codice? e anche uscire dalla tua zona di comfort come sviluppatore.
Ecco cosa costruiremo. Mi sono ispirato alla relativamente nuova app per Twitter per Mac. Se hai l'app (è gratuita), vai a vedere la pagina dell'account di qualcuno. Mentre scorri verso il basso, vedrai che non hanno un avatar alla sinistra di ogni tweet; segue l'avatar per il primo tweet? tu mentre scorri verso il basso. Se incontri un retweet, vedrai che l'avatar della persona retweeted è posizionato appropriatamente accanto al suo tweet. Poi, quando i tweet del tweet iniziano di nuovo, il loro avatar prende il sopravvento.
Questa è la funzionalità che volevo costruire. Con jQuery, questo non sarebbe troppo difficile da mettere insieme, ho pensato. E così ho iniziato.
Certo, prima di poter arrivare alla star dello show, abbiamo bisogno di un po 'di markup con cui lavorare. Non passerò molto tempo qui, perché non è il punto principale di questo tutorial:
Twitter Avatar Scrolling Questo è qualcosa che la persona twitter ha dovuto dire.
Questo è qualcosa che la persona twitter ha dovuto dire.
Questo è qualcosa che la persona twitter ha dovuto dire.
Questo è qualcosa che la persona twitter ha dovuto dire.
Questo è qualcosa che la persona twitter ha dovuto dire.
Questo è qualcosa che la persona twitter ha dovuto dire.
Questo è qualcosa che la persona twitter ha dovuto dire.
Sì, è grande.
Che ne dici di alcuni CSS? Prova questo:
body font: 13px / 1.5 "helvetica neue", helvetica, arial, san-serif; article display: block; sfondo: #ececec; larghezza: 380px; padding: 10px; margin: 10px; overflow: hidden; article img float: left; article p margin: 0; padding-left: 60px;
Abbastanza minimalista, ma ci darà ciò di cui abbiamo bisogno.
Ora, su JavaScript!
Penso che sia giusto dire che questo non è il tuo widget di JavaScript medio; è un po 'più complicato. Qui ci sono solo un paio di cose che devi rendere conto:
Quando inizi a scrivere qualcosa, preoccupati solo di farlo funzionare; l'ottimizzazione può venire dopo. La prima versione ignorava alcune importanti best practice di jQuery. Ciò che iniziamo qui è la seconda versione: il codice jQuery ottimizzato.
Ho deciso di scrivere questo come plugin jQuery, quindi il primo passo è decidere come verrà chiamato; Sono andato con questo:
$ (wrapper_element) .twitter_scroll (entries_element, unscrollable_element);
L'oggetto jQuery che chiamiamo plugin, avvolge i tweet? (o qualunque cosa tu stia scorrendo). Il primo parametro che il plugin prende è un selettore per gli elementi che scorreranno: i "tweet". Il secondo selettore è per gli elementi che restano sul posto quando necessario (il plug-in si aspetta che siano immagini, ma non dovrebbe richiedere molto aggiustamento per farlo funzionare per altri elementi). Quindi, per l'HTML di cui sopra, chiamiamo il plugin in questo modo:
$ ("sezione"). twitter_scroll ("article", ".avatar"); // OR $ ("sezione"). Twitter_scroll (". Avatar");
Come vedrai quando arriveremo al codice, il primo parametro sarà opzionale; se c'è un solo parametro, supponiamo che sia il selettore non convertibile, e le voci siano i diretti genitori degli unscrollables (lo so, unscrollables è un brutto nome, ma è la cosa più generica che potrei inventare).
Quindi, ecco la nostra shell di plugin:
(function ($) jQuery.fn.twitter_scroll = function (entries, unscrollable) ; (jQuery));
D'ora in poi, tutto il codice JavaScript che esamineremo andrà qui.
Iniziamo con il codice di installazione; c'è del lavoro da fare prima di impostare il gestore di scorrimento.
if (! unscrollable) unscrollable = $ (entries); entries = unscrollable.parent (); else unscrollable = $ (unscrollable); voci = $ (voci);
Innanzitutto, i parametri: If unscrollable
è un valore falso-y, lo imposteremo sul valore? jQuerified? inserimenti
selettore e imposta inserimenti
ai genitori di unscrollable
. Altrimenti, lo faremo? entrambi i parametri. È importante notare che ora (se l'utente ha eseguito correttamente il suo markup, che dovremo assumere che hanno), abbiamo due oggetti jQuery in cui le voci corrispondenti hanno lo stesso indice: quindi unscrollable [i]
è il figlio di voci di [i]
. Questo sarà utile in seguito. (Nota: se non volessimo ritenere che l'utente abbia marcato correttamente il loro documento, o che usassero selettori che catturerebbero elementi al di fuori di ciò che vogliamo, potremmo usare Questo
come parametro di contesto o utilizzare il trova
metodo di Questo
.)
Quindi, impostiamo alcune variabili; normalmente, lo farei in alto, ma molti dipendono da unscrollable
e inserimenti
, quindi ci siamo occupati di questo primo:
var parent_from_top = this.offset (). top, entries_from_top = entries.offset (). top, img_offset = unscrollable.offset (), prev_item = null, starter = false, margin = 2, anti_repeater = null, win = $ (finestra ), scroll_top = win.scrollTop ();
Corriamo attraverso questi. Come puoi immaginare, l'offset degli elementi con cui stiamo lavorando qui è importante; jQuery di compensare
metodo restituisce un oggetto con superiore
e sinistra
proprietà offset. Per l'elemento genitore (Questo
all'interno del plugin) e il inserimenti
, avremo solo bisogno dell'offset superiore, quindi lo otterremo. Per gli unscrollables, avremo bisogno sia di top che di left, quindi non otterremo nulla di specifico qui.
Le variabili prev_item
, antipasto
, e anti_repeater
verrà utilizzato in seguito, al di fuori del gestore di scorrimento o all'interno del gestore di scorrimento, dove avremo bisogno di valori che persistono attraverso le chiamate del gestore. Finalmente, vincere
sarà usato in alcuni posti, e scroll_top
è la distanza della barra di scorrimento dalla parte superiore della finestra; lo useremo in seguito per determinare in quale direzione stiamo scorrendo.
Successivamente, determineremo quali elementi sono i primi e gli ultimi in serie di "tweet". Ci sono probabilmente un paio di modi per farlo; lo faremo applicando un attributo di dati HTML5 agli elementi appropriati.
entries.each (function (i) var img = unscrollable [i]; if ($ .contains (this, img)) if (img.src === prev_item) img.style.visibility = "hidden"; if (starter) entries [i-1] .setAttribute ("data-8088-starter", "true"); starter = false; if (! entries [i + 1]) entries [i] .setAttribute ( "data-8088-ender", "true"); else prev_item = img.src; starter = true; if (voci [i-1] && unscrollable [i-1] .style.visibility === " hidden ") entries [i-1] .setAttribute (" data-8088-ender "," true ");); prev_item = null;
Stiamo usando jQuery ogni
metodo sul inserimenti
oggetto. Ricorda che all'interno della funzione passiamo, Questo
si riferisce all'elemento corrente da inserimenti
. Abbiamo anche il parametro index, che useremo. Inizieremo ottenendo l'elemento corrispondente nel file unscrollable
oggetto e memorizzarlo img
. Quindi, se la nostra voce contiene quell'elemento (dovrebbe, ma stiamo solo controllando), controlleremo se l'origine dell'immagine è la stessa di prev_item
; se lo è, sappiamo che questa immagine è uguale a quella della voce precedente. Pertanto, nasconderemo l'immagine; non possiamo usare la proprietà display, perché ciò rimuoverebbe dal flusso del documento; non vogliamo che altri elementi si muovano su di noi.
Quindi, se antipasto
è vero, daremo la voce prima questo è l'attributo Dati-8088-starter
; ricorda che tutti gli attributi dei dati HTML5 devono iniziare con? data- ?; ed è una buona idea aggiungere il proprio prefisso in modo da non entrare in conflitto con il codice degli altri sviluppatori (Il mio prefisso è 8088, dovrete trovarne uno :)). Gli attributi dei dati HTML5 devono essere stringhe, ma nel nostro caso non sono i dati a preoccuparci; abbiamo solo bisogno di segnare quell'elemento Quindi abbiamo impostato antipasto
a falso. Infine, se questa è l'ultima voce, la contrassegneremo come una fine.
Se la fonte dell'immagine non è uguale alla sorgente dell'immagine precedente, verrà ripristinata prev_item
con la fonte della tua immagine attuale; quindi, iniziamo a impostare vero
. In questo modo, se scopriamo che l'immagine successiva ha la stessa fonte di questa, possiamo contrassegnarla come l'iniziatore. Infine, se c'è una voce prima di questa e l'immagine associata è nascosta, sappiamo che la voce è la fine di una serie (perché questa ha un'immagine diversa). Pertanto, gli forniremo un attributo di dati che lo segna come una fine.
Una volta terminato, lo imposteremo prev_item
a nullo
; lo riutilizzeremo presto.
Ora, se guardi le voci in Firebug, dovresti vedere qualcosa di simile a questo:
Ora siamo pronti per lavorare su quel gestore di scroll. Ci sono due parti per questo problema. Innanzitutto, troviamo la voce più vicina alla parte superiore della pagina. In secondo luogo, facciamo tutto ciò che è appropriato per l'immagine relativa a quella voce.
$ (document) .bind ("scroll", function (e) var temp_scroll = win.scrollTop (), down = ((scroll_top - temp_scroll) < 0) ? true : false, top_item = null, child = null; scroll_top = temp_scroll; // more coming );
Questa è la shell del nostro gestore di scroll; abbiamo un paio di variabili che stiamo creando per iniziare; adesso, prendi nota di giù
. Questo sarà vero se stiamo scorrendo verso il basso, falso se stiamo scorrendo verso l'alto. Quindi, stiamo resettando scroll_top
per essere la distanza in giù dalla cima a cui siamo passati.
Ora, impostiamo top_item
per essere la voce più vicina all'inizio della pagina:
top_item = entries.filter (function (i) var distance = $ (this) .offset (). top - scroll_top; return (distanza < (parent_from_top + margin) && distance > (parent_from_top - margin)); );
Questo non è affatto difficile; usiamo solo il filtro
metodo per decidere quale voce vogliamo assegnare top_item
. In primo luogo, otteniamo il distanza
sottraendo l'importo che abbiamo scostato dall'offset superiore della voce. Quindi, restituiamo true se distanza
è tra parent_from_top + margine
e parent_from_top - margine
; altrimenti, falso. Se questo ti confonde, pensaci in questo modo: vogliamo restituire true quando un elemento è proprio in cima alla finestra; in questo caso, distanza
sarebbe uguale a 0. Tuttavia, dobbiamo tenere conto dell'offset superiore del contenitore che avvolge i nostri? tweets? quindi vogliamo davvero distanza
uguale parent_from_top
.
Ma è possibile che il nostro gestore di scorrimento non si attivi quando siamo esattamente su quel pixel, ma invece quando siamo vicini ad esso. L'ho scoperto quando ho registrato la distanza e ho scoperto che erano valori a metà pixel; inoltre, se la tua funzione di gestione dello scorrimento non è troppo efficiente (che questo non sarà, non ancora), è possibile che non si accenda ogni scorrere l'evento. Per essere sicuri di averne uno nella giusta area, aggiungiamo o sottraggiamo un margine per darci un piccolo raggio di lavoro all'interno.
Ora che abbiamo il primo oggetto, facciamo qualcosa con esso.
if (top_item) if (top_item.attr ("data-8088-starter")) if (! anti_repeater) child = top_item.children (unscrollable.selector); anti_repeater = child.clone (). appendTo (document.body); child.css ("visibility", "hidden"); anti_repeater.css ('position': 'fixed', 'top': img_offset.top + 'px', 'left': img_offset.left + "px"); else if (top_item.attr ("data-8088-ender")) top_item.children (unscrollable.selector) .css ("visibility", "visible"); if (anti_repeater) anti_repeater.remove (); anti_repeater = null; if (! down) if (top_item.attr ("data-8088-starter")) top_item.children (unscrollable.selector) .css ("visibility", "visible"); if (anti_repeater) anti_repeater.remove (); anti_repeater = null; else if (top_item.attr ("data-8088-ender")) child = top_item.children (unscrollable.selector); anti_repeater = child.clone (). appendTo (document.body); child.css ("visibility", "hidden"); anti_repeater.css ('position': 'fixed', 'top': img_offset.top + 'px', 'left': img_offset.left + "px");
Potresti notare che il codice sopra riportato è quasi la stessa cosa due volte; ricorda che questa è la versione non ottimizzata. Come ho lentamente risolto il problema, ho iniziato a capire come andare a scorrere verso il basso lavorando; una volta risolto ciò, ho lavorato su per scorrere. Non era immediatamente ovvio, fino a quando tutta la funzionalità era a posto, che ci sarebbe stata molta somiglianza. Ricorda, ottimizzeremo presto.
Quindi, analizziamo questo. Se l'elemento in alto ha un Dati-8088-starter
attributo, quindi, controlliamo per vedere se il anti_repeater
è stato impostato; questa è la variabile che punterà all'elemento dell'immagine che verrà corretto mentre la pagina scorre. Se anti_repeater
non è stato impostato, quindi avremo il bambino il nostro di top_item
voce che ha lo stesso selettore di unscrollable
(no, questo non è un modo intelligente per farlo, lo miglioreremo in seguito). Quindi, lo cloniamo e lo aggiungiamo al corpo. Lo nasconderemo e quindi posizioneremo quello clonato esattamente dove dovrebbe andare.
Se l'elemento non ha a Dati-8088-starter
attributo, controlleremo per a Dati-8088-ender
attributo. Se è lì, troveremo il bambino giusto e renderlo visibile, quindi rimuovere il anti_repeater
e imposta quella variabile su nullo
.
Fortunatamente, se non stiamo andando giù (se stiamo salendo), è esattamente il contrario per i nostri due attributi. E, se il top_item
non ha attributi, siamo nel bel mezzo di una serie e non dobbiamo cambiare nulla.
Bene, questo codice fa quello che vogliamo; tuttavia, se ci provi, noterai che devi scorrere molto lentamente perché funzioni correttamente Prova ad aggiungere le linee console.profile ( "scroll")
e console.profileEnd ()
come prima e ultima riga della funzione di gestione dello scorrimento. Per me, il gestore prende tra 2,5 ms - 4 ms, con 166 - 170 chiamate di funzione in corso.
È troppo lungo per l'esecuzione di un gestore di scorrimento; e, come puoi immaginare, sto eseguendo questo su un computer abbastanza ben dotato. Si noti che alcune funzioni vengono chiamate 30-31 volte; abbiamo 30 voci con cui stiamo lavorando, quindi questo probabilmente fa parte del ciclo su tutti loro per trovare quello in alto. Ciò significa che più voci abbiamo, più lento sarà eseguito; così inefficiente! Quindi, dobbiamo vedere come possiamo migliorare questo.
Se sospetti che jQuery sia il colpevole principale qui, hai ragione. Mentre i framework come jQuery sono incredibilmente utili e rendono il lavoro con il DOM un gioco da ragazzi, hanno un compromesso: le prestazioni. Sì, stanno sempre migliorando; e sì, lo sono anche i browser. Tuttavia, la nostra situazione richiede il codice più veloce possibile e, nel nostro caso, dovremo rinunciare a un po 'di jQuery per alcuni lavori DOM diretti che non sono molto più difficili.
Facciamo la parte ovvia: cosa facciamo con il top_item
una volta che l'abbiamo trovato. Attualmente, top_item
è un oggetto jQuery; tuttavia, tutto ciò che stiamo facendo con jQuery con top_item
è banalmente più difficile senza jQuery, quindi lo faremo? Quando esaminiamo l'acquisizione di top_item
, ci assicureremo che sia un elemento DOM grezzo.
Quindi, ecco cosa possiamo cambiare per renderlo più veloce:
getAttribute
metodo, invece di jQuery attr
.unscrollable
quello corrisponde al top_item
entrata, invece di usare unscrollable.selector
.clodeNode
e appendChild
metodi, invece delle versioni di jQuery.stile
proprietà invece di jQuery css
metodo.removeNode
invece di jQuery rimuovere
.Applicando queste idee, finiamo con questo:
if (top_item) if ((down && top_item.getAttribute ("data-8088-starter")) || (! down && top_item.getAttribute ("data-8088-ender"))) if (! anti_repeater) child = unscrollable [entries.indexOf (top_item)]; anti_repeater = child.cloneNode (false); document.body.appendChild (anti_repeater); child.style.visibility = "hidden"; style = anti_repeater.style; style.position = 'fixed'; style.top = img_offset.top + 'px'; style.left = img_offset.left + 'px'; if ((down && top_item.getAttribute ("data-8088-ender")) || (! down && top_item.getAttribute ("data-8088-starter"))) unscrollable [entries.indexOf (top_item)] .style.visibility = "visible"; if (anti_repeater) anti_repeater.parentNode.removeChild (anti_repeater); anti_repeater = null;
Questo è un codice molto migliore: non solo si sbarazza di quella duplicazione, ma usa anche assolutamente jQuery. (Mentre sto scrivendo questo, penso che potrebbe essere anche un po 'più veloce applicare una classe CSS per lo styling: puoi sperimentare con quello!)
Potresti pensare che sia il massimo che possiamo ottenere; tuttavia, c'è un po 'di ottimizzazione seria che può aver luogo nel processo di acquisizione top_item
. Attualmente stiamo usando jQuery filtro
metodo. Se ci pensi, questo è incredibilmente povero. Sappiamo che recupereremo solo un elemento da questo filtro; ma il filtro
la funzione non lo sa, quindi continua a eseguire elementi attraverso la nostra funzione dopo aver trovato quello che vogliamo. Abbiamo 30 elementi in inserimenti
, quindi è una perdita enorme di tempo. Quello che vogliamo fare è questo:
for (i = 0; entries [i]; i ++) distance = $ (entries [i]). offset (). top - scroll_top; se (distanza < (parent_from_top + margin) && distance > (parent_from_top - margin)) top_item = entries [i]; rompere;
(In alternativa, potremmo usare un ciclo while, con la condizione !top_item
; in entrambi i casi, non importa molto.)
In questo modo, una volta trovato il top_item
, possiamo smettere di cercare. Tuttavia, possiamo fare di meglio; poiché lo scorrimento è una cosa lineare, possiamo prevedere quale elemento sarà più vicino alla cima successiva. Se stiamo scorrendo verso il basso, sarà lo stesso oggetto dell'ultima volta o quello successivo. Se stiamo scorrendo, sarà lo stesso oggetto dell'ultima volta o l'elemento precedente. Ciò sarà utile più in basso nella pagina che otteniamo, perché il ciclo for inizia sempre nella parte superiore della pagina.
Quindi, come possiamo implementarlo? Bene, dobbiamo iniziare tenendo traccia di cosa top_item
era durante la nostra precedente esecuzione del gestore di scorrimento. Possiamo farlo aggiungendo
prev_item = top_item;
fino alla fine della funzione di gestione dello scorrimento. Ora, su dove stavamo trovando in precedenza top_item
, metti questo:
if (prev_item) prev_item = $ (prev_item); altezza = altezza || prev_item.outerHeight (); if (down && prev_item.offset (). top - scroll_top + height < margin) top_item = entries[ entries.indexOf(prev_item[0]) + 1]; else if ( !down && (prev_item.offset().top - scroll_top - height) > (margin + parent_from_top)) top_item = entries [entries.indexOf (prev_item [0]) - 1]; else top_item = prev_item [0]; else for (i = 0; entries [i]; i ++) distance = $ (entries [i]). offset (). top - scroll_top; se (distanza < (parent_from_top + margin) && distance > (parent_from_top - margin)) top_item = entries [i]; rompere;
Questo è sicuramente un miglioramento; se ci fosse un prev_item
, allora possiamo usare quello per trovare il prossimo top_item
. La matematica qui diventa un po 'complicata, ma questo è quello che stiamo facendo:
prev_item
+ 1 per trovare l'elemento giusto in inserimenti
).top_item
, usiamo il nostro ciclo for come fallback.Ci sono alcune altre cose da affrontare qui. Innanzitutto, cos'è questo altezza
variabile? Bene, se tutte le voci hanno la stessa altezza, possiamo evitare di calcolare l'altezza ogni volta all'interno del gestore di scorrimento eseguendolo nel setup. Quindi, ho aggiunto questo al setup:
height = entries.outerHeight (); if (entries.length! == entries.filter (function () return $ (this) .outerHeight () === height;). length) height = null;
jQuery di outerHeight
il metodo ottiene l'altezza di un elemento, inclusi i suoi padding e bordi. Se tutti gli elementi hanno la stessa altezza del primo, allora altezza
sarà impostato; altrimenti altezza
sarà nullo. Quindi, quando si ottiene top_item
, possiamo usare altezza
se è impostato.
L'altra cosa da notare è questa: potresti pensare che sia inefficiente da fare prev_item.offset (). top
due volte; non dovrebbe andare all'interno di una variabile. In realtà, lo faremo solo una volta, perché la seconda istruzione if verrà chiamata solo se giù
è falso; poiché stiamo utilizzando l'operatore AND logico, le seconde parti delle due istruzioni if non verranno mai entrambe eseguite sulla stessa chiamata di funzione. Certo, potresti metterlo in una variabile se pensi che mantenga il tuo codice più pulito, ma non fa nulla per le prestazioni.
Tuttavia, non sono ancora soddisfatto. Certo, il nostro gestore di scroll è più veloce ora, ma possiamo renderlo migliore. Usiamo solo jQuery per due cose: ottenere il outerHeight
di prev_item
e ottenere l'offset superiore di prev_item
. Per qualcosa di così piccolo, è piuttosto costoso creare un oggetto jQuery. Tuttavia, non esiste una soluzione immediatamente apparente, non jQuery, come prima. Quindi, tuffiamoci nel codice sorgente di jQuery per vedere esattamente cosa sta facendo jQuery; in questo modo, possiamo estrarre i bit di cui abbiamo bisogno senza utilizzare il peso costoso di jQuery stesso.
Iniziamo con il problema dell'offset superiore. Ecco il codice jQuery per il compensare
metodo:
function (options) var elem = this [0]; if (! elem ||! elem.ownerDocument) return null; if (options) return this.each (function (i) jQuery.offset.setOffset (this, options, i);); if (elem === elem.ownerDocument.body) return jQuery.offset.bodyOffset (elem); var box = elem.getBoundingClientRect (), doc = elem.ownerDocument, body = doc.body, docElem = doc.documentElement, clientTop = docElem.clientTop || body.clientTop || 0, clientLeft = docElem.clientLeft || body.clientLeft || 0, top = box.top + (self.pageYOffset || jQuery.support.boxModel && docElem.scrollTop || body.scrollTop) - clientTop, left = box.left + (self.pageXOffset || jQuery.support.boxModel && docElem.scrollLeft || body.scrollLeft) - clientLeft; return top: top, left: left;
Questo sembra complicato, ma non è male; abbiamo solo bisogno dell'offset superiore, non dell'offset sinistro, quindi possiamo estrarlo e usarlo per creare la nostra funzione:
function get_top_offset (elem) var doc = elem.ownerDocument, body = doc.body, docElem = doc.documentElement; return elem.getBoundingClientRect (). top + (self.pageYOffset || jQuery.support.boxModel && docElem.scrollTop || body.scrollTop) - docElem.clientTop || body.clientTop || 0;
Tutto ciò che dobbiamo fare è passare nell'elemento DOM grezzo e restituiremo l'offset superiore; grande! quindi possiamo sostituire tutto il nostro offset (). top
chiama con questa funzione.
Che ne dici di outerHeight
? Questo è un po 'più complicato, perché usa una di quelle funzioni jQuery interne multiuso.
function (margin) return this [0]? jQuery.css (questo [0], type, false, margin? "margin": "border"): null;
Come possiamo vedere, questo in realtà fa solo una chiamata al css
funzione, con questi parametri: l'elemento,? height ?,? border ?, e falso
. Ecco come appare:
function (elem, name, force, extra) if (name === "width" || name === "height") var val, props = cssShow, which = name === "width"? cssWidth: cssHeight; function getWH () val = name === "width"? elem.offsetWidth: elem.offsetHeight; if (extra === "border") return; jQuery.each (which, function () if (! extra) val - = parseFloat (jQuery.curCSS (elem, "padding" + this, true)) || 0; if (extra === "margine ") val + = parseFloat (jQuery.curCSS (elem," margin "+ this, true)) || 0; else val - = parseFloat (jQuery.curCSS (elem," border "+ this +" Width ") , vero)) || 0;); if (elem.offsetWidth! == 0) getWH (); else jQuery.swap (elem, props, getWH); restituisce Math.max (0, Math.round (val)); return jQuery.curCSS (elem, name, force);
Potremmo sembrare irrimediabilmente persi a questo punto, ma vale comunque la pena seguire la logica? perché la soluzione è fantastica! Risulta, possiamo semplicemente usare un elemento offsetHeight
proprietà; questo ci dà esattamente quello che vogliamo!
Pertanto, il nostro codice ora si presenta così:
if (prev_item) height = height || prev_item.offsetHeight; if (down && get_top_offset (prev_item) - scroll_top + height < margin) top_item = entries[ entries.indexOf(prev_item) + 1]; else if ( !down && (get_top_offset(prev_item) - scroll_top - height) > (margin + parent_from_top)) top_item = entries [entries.indexOf (prev_item) - 1]; else top_item = prev_item; else for (i = 0; entries [i]; i ++) distance = get_top_offset (entries [i]) - scroll_top; se (distanza < (parent_from_top + margin) && distance > (parent_from_top - margin)) top_item = entries [i]; rompere;
Ora, niente deve essere un oggetto jQuery! E se lo esegui, dovresti essere molto meno di un millisecondo e avere solo un paio di chiamate di funzione.
Prima di concludere, ecco un suggerimento utile per scavare all'interno di jQuery come abbiamo fatto qui: usa il jQuery Source Viewer di James Padolsey. È uno strumento fantastico che rende incredibilmente semplice scavare all'interno del codice e vedere cosa succede senza essere troppo sopraffatto. [Nota di Sid: un altro fantastico strumento da provare - jQuery Deconstructer.]
Spero ti sia piaciuto questo tutorial! Potresti aver imparato come creare un comportamento di scorrimento piuttosto ordinario, ma non era questo il punto principale. Dovresti prendere questi punti di distanza da questo tutorial:
Hai qualche domanda? O forse hai un'idea su come migliorare ancora di più questo! Discutiamone nei commenti!