Dette er noe som kvitteringen måtte si.
jQuery er et fantastisk verktøy, men er det noen gang en gang du ikke bør bruke den? I denne opplæringen skal vi se på hvordan du bygger en interessant rulleoppførsel med jQuery, og deretter gjennomgå hvordan prosjektet vårt potensielt kan forbedres ved å fjerne så mye jQuery eller abstraksjon som mulig.
Noen ganger kan koden din bli enda mer? Magisk? ved å trekke ut noe av det jQuery godhet.
Du kan forvente at dette skal være din normale gjør-noe-kjempebra-med-jQuery opplæring. Egentlig er det ikke. Mens du kanskje ender med å bygge en ganske kult, men ærlig, kanskje like ubrukelig effekt, er det ikke det viktigste punktet jeg vil at du skal ta bort fra denne opplæringen.
Som du forhåpentligvis ser, vil jeg at du skal lære å se på jQuery du skriver som bare vanlig JavaScript, og innse at det ikke er noe magisk om det. Noen ganger kan koden din bli enda mer? Magisk? ved å trekke ut noe av det jQuery godhet. Forhåpentligvis, ved slutten av dette, vil du være litt bedre i å utvikle med JavaScript enn når du startet.
Hvis det høres for abstrakt, anser du dette som en leksjon i ytelse og kode refactoring? og også tråkke utenfor din komfortsone som utvikler.
Her er hva vi skal bygge. Jeg fikk denne inspirasjonen fra den relativt nye Twitter for Mac-appen. Hvis du har appen (den er gratis), kan du se på en persons kontoside. Når du ruller ned, ser du at de ikke har en avatar til venstre for hvert tweet. Avatar for første tweet? følger? deg som du ruller ned. Hvis du møter en retweet, vil du se at den retweetede personens avatar er riktig plassert ved siden av hans eller hennes tweet. Da, når retweeterens tweets begynner igjen, tar deres avatar over.
Dette er funksjonaliteten som jeg ønsket å bygge. Med jQuery ville dette ikke være for vanskelig å sette sammen, tenkte jeg. Og så begynte jeg.
Selvfølgelig, før vi kan komme til showens stjerne, trenger vi noen oppslag for å jobbe med. Jeg vil ikke bruke mye tid her, fordi det ikke er hovedpoenget med denne opplæringen:
Twitter Avatar Rulling Dette er noe som kvitteringen måtte si.
Dette er noe som kvitteringen måtte si.
Dette er noe som kvitteringen måtte si.
Dette er noe som kvitteringen måtte si.
Dette er noe som kvitteringen måtte si.
Dette er noe som kvitteringen måtte si.
Dette er noe som kvitteringen måtte si.
Ja, det er stort.
Hva med noen CSS? Prøv dette:
kropp font: 13px / 1.5 "helvetica neue", helvetica, arial, san-serif; artikkel display: block; bakgrunn: #ececec; bredde: 380px; padding: 10px; margin: 10px; flow: hidden; artikkel img float: left; artikkel p margin: 0; padding-left: 60 px;
Ganske minimal, men det vil gi oss det vi trenger.
Nå, videre til JavaScript!
Jeg synes det er rimelig å si at dette ikke er det gjennomsnittlige JavaScript-widget-arbeidet ditt; det er litt mer komplisert. Her er bare noen av de tingene du trenger å ta hensyn til:
Når du begynner å skrive noe, bekymre deg for bare å få det til å fungere; optimalisering kan komme senere. Versjon 1 ignorert flere viktige jQuery-best practices. Det vi starter med her er versjon to: den optimaliserte jQuery-koden.
Jeg bestemte meg for å skrive dette som en jQuery-plugin, så det første trinnet er å bestemme hvordan det vil bli kalt; Jeg gikk med dette:
$ (wrapper_element) .twitter_scroll (entries_element, unscrollable_element);
Det jQuery-objektet vi kaller pluginet på, bryter inn tweets? (eller hva du ruller gjennom). Den første parameteren plugin tar er en velg for elementene som skal rulle: de? Tweets.? Den andre velgeren er for elementene som holdes på plass når det er nødvendig (pluginet forventer at disse skal være bilder, men det bør ikke ta mye justering for å få det til å fungere for andre elementer). Så, for HTML-koden vi hadde over, kaller vi pluginet slik:
$ ("section"). twitter_scroll ("artikkel", ".avatar"); // OR $ ("section"). Twitter_scroll (". Avatar");
Som du ser når vi kommer til koden, vil den første parameteren være valgfri; hvis det bare er én parameter, antar vi at det er den unscrollable selektoren, og oppføringene er de direkte foreldrene til unscrollables (jeg vet, unscrollables er et dårlig navn, men det er den mest generiske tingen jeg kunne komme opp med).
Så, her er vår plugin shell:
(funksjon ($) jQuery.fn.twitter_scroll = funksjon (oppføringer, unscrollable) ; (jQuery));
Fra nå av vil alt JavaScript vi ser på, gå inn her.
La oss starte med oppsettkoden; Det er litt arbeid å gjøre før vi konfigurerer rullehåndtereren.
hvis (! unscrollable) unscrollable = $ (oppføringer); oppføringer = unscrollable.parent (); ellers unscrollable = $ (unscrollable); oppføringer = $ (oppføringer);
Først parametrene: Hvis unscrollable
er en false-y-verdi, vi stiller den til? jQuerified? innganger
velg og sett innganger
til foreldrene til unscrollable
. Ellers vil vi? JQuerify? begge parametrene. Det er viktig å legge merke til at nå (hvis brukeren har gjort sin markering riktig, som vi må anta at de har), har vi to jQuery-objekter der de tilsvarende oppføringene har samme indeks: så unscrollable [i]
er barn av oppføringer [i]
. Dette vil være nyttig senere. (Merk: Hvis vi ikke ville anta at brukeren merket dokumentet sitt riktig, eller at de brukte selektorer som ville fange elementer utenfor det vi ønsket, kunne vi bruke dette
som kontekstparameter, eller bruk finne
Metode av dette
.)
La oss da stille noen variabler; Normalt vil jeg gjøre dette rett på toppen, men flere avhenger av unscrollable
og innganger
, så vi behandlet det først:
var parent_from_top = this.offset (). topp, entries_from_top = entries.offset (). topp, img_offset = unscrollable.offset (), prev_item = null, starter = false, margin = 2, anti_repeater = null, win = $ ), scroll_top = win.scrollTop ();
La oss løpe gjennom disse. Som du kanskje tror, er kompensasjonen av elementene vi jobber med her viktige; jQuery offset
Metode returnerer et objekt med topp
og venstre
offset egenskaper. For foreldreelementet (dette
inne i pluginet) og innganger
, Vi trenger bare det beste offsetet, så får vi det. For unscrollables trenger vi både topp og venstre, så vi får ikke noe spesifikt her.
Variablene prev_item
, starter
, og anti_repeater
vil bli brukt senere, enten utenfor rullehåndteringen eller inne i rullehåndteringen, der vi trenger verdier som vedvarer over kallelser fra handleren. Endelig, vinne
vil bli brukt på noen få steder, og scroll_top
er avstanden til rullefeltet fra toppen av vinduet; Vi bruker dette senere for å bestemme hvilken retning vi ruller inn.
Deretter skal vi avgjøre hvilke elementer som er første og sist i streker av? Tweets.? Det er nok et par måter å gjøre dette på; Vi skal gjøre dette ved å bruke et HTML5-dataattributt til de aktuelle elementene.
entries.each (funksjon (i) var img = unscrollable [i]; hvis ($ .contains (this, img)) if (img.src === prev_item) img.style.visibility = "hidden"; hvis (starter) oppføringer [i-1] .setAttribute ("data-8088-starter", "sant"); starter = false; hvis (! oppføringer [i + 1]) oppføringer [i] .setAttribute "data-8088-ender", "true"); else prev_item = img.src; starter = true; hvis (oppføringer [i-1] && unscrollable [i-1] .style.visibility === " skjult ") oppføringer [i-1] .setAttribute (" data-8088-ender "," true ");); prev_item = null;
Vi bruker jQuery's Hver
metode på innganger
gjenstand. Husk at inne i funksjonen passerer vi inn, dette
refererer til det nåværende elementet fra innganger
. Vi har også indeksparameteren, som vi skal bruke. Vi begynner med å få det tilsvarende elementet i unscrollable
objekt og lagre den inn img
. Så, hvis oppføringen inneholder det elementet (det skal, men vi bare sjekker), vil vi sjekke for å se om kilden til bildet er det samme som prev_item
; Hvis det er, vet vi at dette bildet er det samme som det i forrige oppføring. Derfor vil vi gjemme bildet; Vi kan ikke bruke skjermegenskapen fordi det ville fjerne det fra dokumentets strømning; vi vil ikke ha andre elementer som beveger seg på oss.
Så, hvis starter
er sant, vi gir oppføringen før denne en attributtet data-8088-starter
; husk, alle HTML5 data attributter må starte med? data- ?; og det er en god ide å legge til eget prefiks slik at du ikke vil komme i konflikt med andre utvikleres kode (Mitt prefiks er 8088, du må finne din egen :)). HTML5-dataattributter må være strenger, men i vårt tilfelle er det ikke dataene vi er bekymret for; vi trenger bare å markere det elementet. Så setter vi starter
til falsk. Endelig, hvis dette er den siste oppføringen, merker vi det som en slutt.
Hvis kilden til bildet ikke er det samme som kilden til det forrige bildet, resettes vi prev_item
med kilden til ditt nåværende bilde; så setter vi starter til ekte
. På den måten, hvis vi finner at neste bilde har samme kilde som denne, kan vi markere denne som startbilde. Til slutt, hvis det er en oppføring før denne, og det tilhørende bildet er skjult, vet vi at oppføringen er enden av et strek (fordi denne har et annet bilde). Derfor gir vi det et datatributt som markerer det som en slutt.
Når vi er ferdig med det, setter vi inn prev_item
til null
; Vi vil gjenbruke det snart.
Nå, hvis du ser på oppføringene i Firebug, bør du se noe slikt:
Nå er vi klare til å jobbe på den rullehåndtereren. Det er to deler til dette problemet. Først finner vi oppføringen som er nærmest toppen av siden. For det andre gjør vi hva som passer for bildet relatert til den oppføringen.
$ (dokument) .bind ("scroll", funksjon (e) var temp_scroll = win.scrollTop (), ned = ((scroll_top - temp_scroll) < 0) ? true : false, top_item = null, child = null; scroll_top = temp_scroll; // more coming );
Dette er vår rullehåndteringsskall; Vi har et par variabler som vi lager for å begynne med; akkurat nå, ta oppmerksom på ned
. Dette vil være sant hvis vi ruller ned, falsk hvis vi ruller opp. Så resetter vi oss scroll_top
å være avstanden ned fra toppen som vi er rullet til.
La oss nå stille top_item
for å være posten nærmest toppen av siden:
top_item = entries.filter (funksjon (i) var avstand = $ (dette). offset (). topp - scroll_top; returnere (avstand < (parent_from_top + margin) && distance > (parent_from_top - margin)); );
Dette er ikke vanskelig i det hele tatt; vi bruker bare filter
metode for å bestemme hvilken oppføring vi ønsker å tilordne seg top_item
. Først får vi avstand
ved å trekke ut beløpet vi har rullet fra det øverste offsettet for oppføringen. Så returnerer vi sant hvis avstand
er mellom parent_from_top + margin
og parent_from_top - margin
; ellers falsk. Hvis dette forvirrer deg, tenk på det på denne måten: Vi vil returnere sant når et element er rett øverst i vinduet; i dette tilfellet, avstand
ville være 0. Vi må imidlertid ta hensyn til den største forskyvningen av beholderen som pakker våre tweets ,? så vi virkelig vil avstand
å like parent_from_top
.
Men det er mulig at vår rullehåndterer ikke vil brenne når vi er akkurat på den piksel, men i stedet når vi er i nærheten av den. Jeg oppdaget dette da jeg logget avstanden og fant det å være verdier som var en halv pixel av; også, hvis du ikke er for effektiv på rullelagringsfunksjonen (som denne ikke vil være, ennå ikke), er det mulig at den ikke brenner på hver bla hendelsen. For å sikre at du får en i riktig område, legger vi til eller trekker en margin for å gi oss et lite utvalg å jobbe innenfor.
Nå som vi har det øverste elementet, la oss gjøre noe med det.
hvis (top_item) if (top_item.attr ("data-8088-starter")) hvis (! anti_repeater) child = top_item.children (unscrollable.selector); anti_repeater = child.clone (). appendTo (document.body); child.css ("synlighet", "skjult"); anti_repeater.css ('posisjon': 'fast', 'topp': img_offset.top + 'px', 'left': img_offset.left + "px"); annet hvis (top_item.attr ("data-8088-ender")) top_item.children (unscrollable.selector) .css ("synlighet", "synlig"); hvis (anti_repeater) anti_repeater.remove (); anti_repeater = null; hvis (! ned) if (top_item.attr ("data-8088-starter")) top_item.children (unscrollable.selector) .css ("synlighet", "synlig"); hvis (anti_repeater) anti_repeater.remove (); anti_repeater = null; annet hvis (top_item.attr ("data-8088-ender")) child = top_item.children (unscrollable.selector); anti_repeater = child.clone (). appendTo (document.body); child.css ("synlighet", "skjult"); anti_repeater.css ('posisjon': 'fast', 'topp': img_offset.top + 'px', 'left': img_offset.left + "px");
Du kan legge merke til at koden ovenfor er nesten det samme to ganger; Husk at dette er den ikke-optimaliserte versjonen. Da jeg langsomt arbeidet gjennom problemet, begynte jeg å finne ut hvordan jeg rullet ned arbeidet; Når jeg hadde løst det, jobbet jeg med å rulle opp. Det var ikke umiddelbart opplagt, før all funksjonalitet var på plass, at det ville være så mye likhet. Husk, vi optimaliserer snart.
Så, la oss dissekere dette. Hvis toppunktet har en data-8088-starter
attributt, så la oss sjekke for å se om anti_repeater
har blitt satt; Dette er variabelen som vil peke på bildeelementet som blir løst mens siden ruller. Hvis anti_repeater
har ikke blitt satt, så får vi barnet vårt av top_item
oppføring som har samme velger som unscrollable
(nei, dette er ikke en smart måte å gjøre dette på, vi vil forbedre det senere). Da kloner vi det og legger det til kroppen. Vi vil gjemme den, og plasser den klonede en nøyaktig hvor den skal gå.
Hvis elementet ikke har en data-8088-starter
attributt, vi vil sjekke for en data-8088-ender
Egenskap. Hvis det er der, finner vi det riktige barnet og gjør det synlig, og fjern deretter anti_repeater
og sett den variabelen til null
.
Heldigvis, hvis vi ikke går ned (hvis vi går opp), er det helt bakover for våre to attributter. Og hvis top_item
har ikke noen attributter, vi er et sted midt i et strek, og trenger ikke å endre noe.
Vel, denne koden gjør det vi vil ha; Men hvis du gir den en prøve, vil du legge merke til at du må bla veldig sakte for at den skal fungere skikkelig. Prøv å legge til linjene console.profile ( "rull")
og console.profileEnd ()
som de første og siste linjene i rullehåndteringsfunksjonen. For meg tar handler mellom 2,5 ms - 4 ms, med 166 - 170 funksjonssamtaler finner sted.
Det er altfor lang tid for en rullehåndterer å løpe; og som du kanskje tror, kjører jeg dette på en rimelig velstelte datamaskin. Legg merke til at noen funksjoner blir kalt 30-31 ganger; Vi har 30 oppføringer som vi jobber med, så dette er trolig en del av loopingen over dem alle for å finne den øverste. Dette betyr at jo flere oppføringer vi har, jo langsommere vil dette løpe; så ineffektiv! Så må vi se hvordan vi kan forbedre dette.
Hvis du mistenker at jQuery er den viktigste skyldige her, har du rett. Mens rammer som jQuery er awesomely helpful og gjør jobber med DOM en bris, kommer de med en avgang: ytelse. Ja, de blir alltid bedre; og ja, det er også nettleserne. Men vår situasjon krever den raskeste mulige koden, og i vårt tilfelle må vi gi opp litt jQuery for noen direkte DOM-arbeid som ikke er for mye vanskeligere.
La oss med den åpenbare delen: hva vi gjør med top_item
når vi har funnet den. For tiden, top_item
er et jQuery-objekt; Men alt vi gjør med jQuery med top_item
er trivielt vanskeligere uten jQuery, så vi skal gjøre det? rå.? Når vi vurderer å få av top_item
, Vi sørger for at det er et rå DOM-element.
Så, her er hva vi kan forandre for å gjøre det raskere:
getAttribute
metode, i stedet for jQuery's attr
.unscrollable
som tilsvarer top_item
oppføring, i stedet for å bruke unscrollable.selector
.clodeNode
og appendChild
metoder, i stedet for jQuery-versjonene.stil
eiendom i stedet for jQuery s css
metode.removeNode
i stedet for jQuery's fjerne
.Ved å bruke disse ideene, slutter vi med dette:
hvis (top_item) if ((og & top_item.getAttribute ("data-8088-starter")) || (! ned && top_item.getAttribute ("data-8088-ender"))) if (! anti_repeater) barn = unscrollable [entries.indexOf (top_item)]; anti_repeater = child.cloneNode (false); document.body.appendChild (anti_repeater); child.style.visibility = "hidden"; stil = anti_repeater.style; style.position = 'fixed'; style.top = img_offset.top + 'px'; style.left = img_offset.left + 'px'; hvis ((ned && top_item.getAttribute ("data-8088-ender")) || (! ned && top_item.getAttribute ("data-8088-starter"))) unscrollable [entries.indexOf (top_item)] .style.visibility = "visible"; hvis (anti_repeater) anti_repeater.parentNode.removeChild (anti_repeater); anti_repeater = null;
Dette er mye bedre kode: ikke bare blir det kvitt dupliseringen, men det bruker også absolutt ingen jQuery. (Når jeg skriver dette, tenker jeg kanskje det kan være litt raskere å bruke en CSS-klasse for å gjøre stylingen, du kan eksperimentere med det!)
Du tror kanskje det handler om så godt som vi kan få; Det er imidlertid noen seriøse optimaliseringer som kan finne sted når man kommer top_item
. For tiden bruker vi jQuery's filter
metode. Hvis du tenker på det, er dette utrolig dårlig. Vi vet at vi bare kommer til å få ett element tilbake fra denne filtreringen; men filter
funksjonen vet ikke det, så det fortsetter å kjøre elementer gjennom vår funksjon etter at vi har funnet den vi ønsker. Vi har 30 elementer i innganger
, så det er et ganske stort sløsing med tid. Det vi vil gjøre er dette:
for (i = 0; oppføringer [i]; i ++) avstand = $ (oppføringer [i]). offset (). topp - scroll_top; hvis (avstand < (parent_from_top + margin) && distance > (parent_from_top - margin)) top_item = oppføringer [i]; gå i stykker;
(Alternativt kan vi bruke en stundsløyfe, med tilstanden !top_item
; Uansett, det spiller ingen rolle.)
På denne måten, når vi finner top_item
, vi kan slutte å søke. Men vi kan gjøre det bedre; fordi rulling er en lineær ting, kan vi forutsi hvilket element som er nærmest toppen neste. Hvis vi ruller ned, vil det enten være det samme elementet som sist, eller den etter det. Hvis vi ruller opp, blir det samme element som forrige gang, eller elementet før det. Dette vil være nyttig jo lenger ned på siden vi får, fordi forløpet alltid starter øverst på siden.
Så hvordan kan vi implementere dette? Vel, vi må begynne med å holde rede på hva top_item
var under vår tidligere drift av rullelederen. Vi kan gjøre dette ved å legge til
prev_item = top_item;
helt til slutten av rullehåndteringsfunksjonen. Nå opp der vi tidligere var å finne top_item
, sett dette:
hvis (prev_item) prev_item = $ (prev_item); høyde = høyde || prev_item.outerHeight (); hvis (ned && prev_item.offset (). topp - scroll_top + høyde < 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 = oppføringer [entries.indexOf (prev_item [0]) - 1]; else top_item = prev_item [0]; ellers for (i = 0; oppføringer [i]; i ++) avstand = $ (oppføringer [i]). offset (). topp - scroll_top; hvis (avstand < (parent_from_top + margin) && distance > (parent_from_top - margin)) top_item = oppføringer [i]; gå i stykker;
Dette er definitivt en forbedring; hvis det var en prev_item
, så kan vi bruke det for å finne det neste top_item
. Matematikken her blir litt vanskelig, men dette er hva vi gjør:
prev_item
+ 1 for å finne det rette elementet i innganger
).top_item
, vi bruker vår forløp som en tilbakebetaling.Det er noen andre ting å ta opp her. For det første, hva er dette høyde
variabel? Vel, hvis alle våre oppføringer er i samme høyde, kan vi unngå å beregne høyden hver gang i rullehåndtereren ved å gjøre det i oppsettet. Så, jeg har lagt til dette i oppsettet:
høyde = entries.outerHeight (); hvis (entries.length! == entries.filter (funksjon () return $ (this) .outerHeight () === høyde;). lengde) height = null;
jQuery outerHeight
Metoden får høyden på et element, inkludert det er polstring og grenser. Hvis alle elementene har samme høyde som den første, da høyde
vil bli satt; ellers høyde
vil være null. Så når du får det top_item
, Vi kan bruke høyde
hvis den er satt.
Den andre tingen å merke seg er dette: du tror kanskje det er ineffektivt å gjøre prev_item.offset (). Den beste
to ganger; bør ikke det gå inn i en variabel. Faktisk vil vi bare gjøre det en gang, fordi den andre hvis setningen bare blir kalt hvis ned
er falsk; fordi vi bruker den logiske AND-operatøren, vil de andre delene av de to om setningene aldri begge bli kjørt på samme funksjonsanrop. Selvfølgelig kan du sette den i en variabel hvis du tror det holder koden renere, men det gjør ingenting for ytelse.
Imidlertid er jeg fortsatt ikke fornøyd. Jo, vår rullehåndterer er raskere nå, men vi kan gjøre det bedre. Vi bruker bare jQuery for to ting: å få tak i outerHeight
av prev_item
og for å få topp offset av prev_item
. For noe denne lille, er det ganske dyrt å lage et jQuery-objekt. Det er imidlertid ikke en umiddelbart åpenbar, ikke-jQuery løsning på disse, som det var før. Så, la oss dykke inn i jQuery-kildekoden for å se nøyaktig hva jQuery gjør; På den måten kan vi trekke ut biter vi trenger uten å bruke den dyre vekten av jQuery selv.
La oss starte med det største offsetproblemet. Her er jQuery-koden for offset
metode:
funksjon (alternativer) var elem = dette [0]; hvis (! elem ||! elem.ownerDocument) return null; hvis (alternativer) return this.each (funksjon (i) jQuery.offset.setOffset (dette alternativene, i);); hvis (elem === elem.ownerDocument.body) return jQuery.offset.bodyOffset (elem); var boks = elem.getBoundingClientRect (), doc = elem.ownerDocument, body = doc.body, docElem = doc.documentElement, clientTop = docElem.clientTop || body.clientTop || 0, clientLeft = docElem.clientLeft || body.clientLeft || 0, topp = 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; returnere topp: topp, venstre: venstre;
Dette ser komplisert ut, men det er ikke så ille; Vi trenger bare den øverste forskyvningen, ikke det venstre offsetet, så vi kan trekke dette ut og bruke det til å skape vår egen funksjon:
funksjon get_top_offset (elem) var doc = elem.ownerDocument, body = doc.body, docElem = doc.documentElement; returnere elem.getBoundingClientRect (). toppen + (self.pageYOffset || jQuery.support.boxModel && docElem.scrollTop || body.scrollTop) - docElem.clientTop || body.clientTop || 0;
Alt vi trenger å gjøre det, passerer i det rå DOM-elementet, og vi får toppforskyvningen tilbake; flott! så vi kan erstatte alle våre forskyvning (). Den beste
samtaler med denne funksjonen.
Hva med outerHeight
? Denne er litt vanskeligere, fordi den bruker en av disse multi-bruk interne jQuery-funksjonene.
funksjon (margin) returner dette [0]? jQuery.css (denne [0], type, false, margin? "margin": "border"): null;
Som vi kan se, gjør dette faktisk bare et anrop til css
funksjon, med disse parameterne: elementet,? høyde ?,? grense ?, og falsk
. Her ser det ut som:
funksjon (elem, navn, kraft, ekstra) if (name === "width" || navn === "høyde") var val, props = cssShow, hvilke = navn === "bredde"? cssWidth: cssHeight; funksjon getWH () val = navn === "bredde"? elem.offsetWidth: elem.offsetHeight; hvis (ekstra === "grense") return; jQuery.each (som, funksjon () hvis (! ekstra) val - = parseFloat (jQuery.curCSS (elem, "padding" + dette, sant)) || 0; hvis (ekstra === "margin ") val + = parseFloat (jQuery.curCSS (elem," margin "+ dette, sant)) || 0; else val - = parseFloat (jQuery.curCSS (elem," border "+ this +" Width " , sant)) || 0;); hvis (elem.offsetWidth! == 0) getWH (); ellers jQuery.swap (elem, rekvisitter, getWH); returner Math.max (0, Math.round (val)); returnere jQuery.curCSS (elem, navn, kraft);
Vi kan virke håpløst tapt på dette tidspunktet, men det er verdt det å følge logikken skjønt? fordi løsningen er kjempebra! Vises, vi kan bare bruke et element offsetHeight
eiendom; Det gir oss akkurat det vi ønsker!
Derfor ser vår kode nå slik ut:
hvis (prev_item) height = height || prev_item.offsetHeight; hvis (ned && get_top_offset (prev_item) - scroll_top + høyde < 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 = oppføringer [entries.indexOf (prev_item) - 1]; annet top_item = prev_item; ellers for (i = 0; oppføringer [i]; i ++) distance = get_top_offset (oppføringer [i]) - scroll_top; hvis (avstand < (parent_from_top + margin) && distance > (parent_from_top - margin)) top_item = oppføringer [i]; gå i stykker;
Nå må ingenting være et jQuery-objekt! Og hvis du kjører det ned, bør du være langt under en millisekund og bare ha et par funksjonssamtaler.
Før vi konkluderer, er det et nyttig tips for å grave rundt i jQuery som vi gjorde her: bruk James Padolsey's jQuery Source Viewer. Det er et fantastisk verktøy som gjør det utrolig enkelt å grave rundt i koden og se hva som foregår uten å være for overveldet. [Sidens notat: Et annet fantastisk verktøy du bør sjekke - jQuery Deconstructer.]
Jeg håper du har hatt denne opplæringen! Du har kanskje lært hvordan du lager litt ganske ryddig oppførsel, men det var ikke hovedpunktet. Du bør ta disse punktene bort fra denne opplæringen:
Har du spørsmål? Eller kanskje du har en ide om hvordan du kan forbedre dette enda mer! La oss diskutere det i kommentarene!