C'est quelque chose que la personne twitter avait à dire.
jQuery est un outil génial, mais y a-t-il un moment où vous ne devriez pas l'utiliser? Dans ce tutoriel, nous allons voir comment créer un comportement de défilement intéressant avec jQuery, puis voir comment notre projet pourrait être amélioré en supprimant autant que possible jQuery ou abstraction..
Parfois, votre code peut être encore plus "magique"? en soustrayant une partie de cette bonté jQuery.
Vous pouvez vous attendre à ce que ce soit votre didacticiel habituel sur le do-quelque chose de génial avec jQuery. En fait, ce n'est pas. Bien que vous puissiez en arriver à créer un effet plutôt cool, mais franchement, peut-être tout aussi inutile, ce n'est pas là le point principal que je voudrais que vous reteniez de ce tutoriel..
Comme vous le verrez avec espoir, je veux que vous appreniez à regarder le jQuery que vous écrivez comme un simple JavaScript, et à vous rendre compte qu'il n'y a rien de magique à cela. Parfois, votre code peut être encore plus "magique"? en soustrayant une partie de cette bonté jQuery. J'espère qu'à la fin de cette partie, votre développement avec JavaScript sera un peu meilleur que lorsque vous avez commencé..
Si cela semble trop abstrait, considérez ceci comme une leçon de performance et de refactoring de code? et aussi sortir de votre zone de confort en tant que développeur.
Voici ce que nous allons construire. J'ai eu cette inspiration de la nouvelle application Twitter pour Mac. Si vous avez l'application (c'est gratuit), allez voir la page du compte de quelqu'un. En faisant défiler l'écran, vous verrez qu'ils n'ont pas d'avatar à la gauche de chaque tweet; l'avatar du premier tweet? suit? vous comme vous faites défiler. Si vous rencontrez un retweet, vous verrez que l'avatar de la personne retweetée est placé de manière appropriée à côté de son tweet. Puis, lorsque les tweets du retweeter recommencent, leur avatar prend le relais.
C'est la fonctionnalité que je voulais construire. Avec jQuery, ce ne serait pas trop difficile à organiser, pensai-je. Et alors j'ai commencé.
Bien sûr, avant d’arriver à la vedette de la série, nous avons besoin d’une certaine marge de manœuvre. Je ne passerai pas beaucoup de temps ici, car ce n’est pas l’essentiel de ce tutoriel:
Défilement de l'avatar sur Twitter C'est quelque chose que la personne twitter avait à dire.
C'est quelque chose que la personne twitter avait à dire.
C'est quelque chose que la personne twitter avait à dire.
C'est quelque chose que la personne twitter avait à dire.
C'est quelque chose que la personne twitter avait à dire.
C'est quelque chose que la personne twitter avait à dire.
C'est quelque chose que la personne twitter avait à dire.
Oui, c'est grand.
Que diriez-vous de CSS? Essaye ça:
corps font: 13px / 1.5 "helvetica neue", helvetica, arial, san-serif; article display: block; arrière-plan: #ecec; largeur: 380px; rembourrage: 10px; marge: 10px; débordement caché; article img float: left; article p margin: 0; rembourrage à gauche: 60px;
Assez minime, mais cela nous donnera ce dont nous avons besoin.
Passons maintenant au JavaScript!
Je pense qu'il est juste de dire que ce n'est pas votre travail de widget JavaScript moyen; c'est un peu plus compliqué. Voici quelques éléments à prendre en compte:
Lorsque vous commencez à écrire quelque chose, vous avez peur de le faire fonctionner; l'optimisation peut venir plus tard. La première version ignorait plusieurs des meilleures pratiques importantes de jQuery. Nous commençons ici avec la version deux: le code jQuery optimisé.
J'ai décidé d'écrire ceci comme un plugin jQuery, donc la première étape consiste à décider comment il sera appelé. Je suis allé avec ça:
$ (wrapper_element) .twitter_scroll (entries_element, unscrollable_element);
L'objet jQuery sur lequel nous appelons le plugin, enveloppe les? Tweets? (ou tout ce que vous faites défiler). Le premier paramètre pris par le plugin est un sélecteur pour les éléments qui défileront: les? Tweets.? Le second sélecteur est destiné aux éléments qui restent en place lorsque cela est nécessaire (le plugin s'attend à ce qu'il s'agisse d'images, mais cela ne devrait pas nécessiter beaucoup d'ajustement pour que cela fonctionne avec d'autres éléments). Donc, pour le HTML que nous avons eu ci-dessus, nous appelons le plugin comme suit:
$ ("section"). twitter_scroll ("article", ".avatar"); // OU $ ("section"). Twitter_scroll (". Avatar");
Comme vous le verrez lorsque nous arriverons au code, le premier paramètre sera optionnel; s'il n'y a qu'un seul paramètre, nous supposerons que c'est le sélecteur inviolable, et que les entrées sont les parents directs des inviolables (je sais, inviolables est un mauvais nom, mais c'est la chose la plus générique que je pourrais trouver).
Alors, voici notre plugin shell:
(fonction ($) jQuery.fn.twitter_scroll = fonction (entrées, inviolables) ; (jQuery));
A partir de maintenant, tout le JavaScript que nous allons voir va ici.
Commençons par le code d'installation. il y a du travail à faire avant de configurer le gestionnaire de défilement.
if (! unscrollable) unscrollable = $ (entrées); entrées = unscrollable.parent (); else unscrollable = $ (unscrollable); entrées = $ (entrées);
Tout d'abord, les paramètres: Si inviolable
est une valeur fausse-y, nous allons la définir sur le? jQuerified? les entrées
sélecteur et set les entrées
aux parents de inviolable
. Sinon, nous allons? JQuerify? les deux paramètres. Il est important de noter que maintenant (si l'utilisateur a correctement fait son balisage, ce qu'il faudra supposer qu'il a fait), nous avons deux objets jQuery dans lesquels les entrées correspondantes ont le même index: inviolable [i]
est l'enfant de entrées [i]
. Cela sera utile plus tard. (Remarque: si nous ne voulions pas supposer que l'utilisateur marquait correctement son document, ou qu'il utilisait des sélecteurs permettant de capturer des éléments extérieurs à ce que nous souhaitons, nous pourrions utiliser ce
comme paramètre de contexte, ou utilisez le trouver
méthode de ce
.)
Ensuite, définissons des variables; normalement, je le ferais tout en haut, mais plusieurs dépendent de inviolable
et les entrées
, donc nous avons traité cela en premier:
var parent_from_top = this.offset (). top, entries_from_top = entrées.offset (). top, img_offset = unscrollable.offset (), prev_item = null, starter = false, margin = 2, anti_repeater = null, win = $ (fenêtre) ), scroll_top = win.scrollTop ();
Courons à travers ceux-ci. Comme vous pouvez l’imaginer, la compensation des éléments avec lesquels nous travaillons est importante; jQuery's décalage
La méthode retourne un objet avec Haut
et la gauche
propriétés de décalage. Pour l'élément parent (ce
dans le plugin) et le les entrées
, nous aurons seulement besoin du décalage supérieur, donc nous l'obtiendrons. Pour les objets inviolables, nous aurons besoin de haut et de gauche, nous n'obtiendrons rien de spécifique ici..
Les variables prev_item
, entrée
, et anti-répéteur
sera utilisé plus tard, soit en dehors du gestionnaire de défilement ou à l'intérieur du gestionnaire de défilement, où nous aurons besoin de valeurs qui persistent lors d'appels du gestionnaire. finalement, gagner
sera utilisé dans quelques endroits, et scroll_top
est la distance de la barre de défilement à partir du haut de la fenêtre; nous l'utiliserons plus tard pour déterminer dans quelle direction nous faisons défiler.
Ensuite, nous allons déterminer quels éléments sont les premiers et les derniers dans la série de "tweets". Il y a probablement plusieurs façons de le faire. nous allons faire cela en appliquant un attribut de données HTML5 aux éléments appropriés.
entries.each (function (i) var img = non-adhérent [i]; if ($ .contains (this, img)) if (img.src === prev_item) img.style.visibility = "caché"; if (starter) entrées [i-1] .setAttribute ("data-8088-starter", "true"); starter = false; if (! entrées [i + 1]) entrées [i] .setAttribute ( "data-8088-ender", "true"); else prev_item = img.src; starter = true; if (entrées [i-1] && unscrollable [i-1] .style.visibility === " hidden ") entrées [i-1] .setAttribute (" data-8088-ender "," true ");); prev_item = null;
Nous utilisons jQuery chaque
méthode sur le les entrées
objet. Rappelez-vous que dans la fonction que nous passons, ce
fait référence à l'élément actuel de les entrées
. Nous avons également le paramètre index, que nous utiliserons. Nous allons commencer par obtenir l'élément correspondant dans le inviolable
objet et le stocker dans img
. Ensuite, si notre entrée contient cet élément (cela devrait être le cas, mais nous vérifions simplement), nous vérifierons si la source de l'image est la même que prev_item
; si c'est le cas, nous savons que cette image est identique à celle de l'entrée précédente. Par conséquent, nous allons cacher l'image; nous ne pouvons pas utiliser la propriété display, car cela la supprimerait du flux du document; nous ne voulons pas que d'autres éléments bougent sur nous.
Puis si entrée
est vrai, nous allons donner l'entrée avant celui-ci l'attribut data-8088-starter
; Rappelez-vous que tous les attributs de données HTML5 doivent commencer par? data- ?; et c'est une bonne idée d'ajouter votre propre préfixe afin de ne pas entrer en conflit avec le code des autres développeurs (mon préfixe est 8088; vous devrez trouver le vôtre :)). Les attributs de données HTML5 doivent être des chaînes, mais dans notre cas, ce ne sont pas les données qui nous préoccupent; nous avons juste besoin de marquer cet élément. Puis nous avons mis entrée
à faux. Enfin, s'il s'agit de la dernière entrée, nous la marquerons comme une fin.
Si la source de l'image n'est pas la même que celle de l'image précédente, nous réinitialiserons prev_item
avec la source de votre image actuelle; alors, nous mettons le démarreur à vrai
. De cette façon, si nous trouvons que l'image suivante a la même source, nous pouvons marquer celle-ci comme démarreur. Enfin, s'il y a une entrée avant celle-ci et que son image associée est masquée, nous savons que l'entrée est la fin d'une série (car celle-ci a une image différente). Par conséquent, nous lui donnerons un attribut de données le marquant comme une fin.
Une fois que nous aurons fini cela, nous définirons prev_item
à nul
; nous allons bientôt le réutiliser.
Maintenant, si vous regardez les entrées dans Firebug, vous devriez voir quelque chose comme ça:
Nous sommes maintenant prêts à travailler sur ce gestionnaire de défilement. Ce problème comporte deux parties. Tout d'abord, nous trouvons l'entrée la plus proche du haut de la page. Deuxièmement, nous faisons tout ce qui est approprié pour l'image liée à cette entrée.
$ (document) .bind ("scroll", fonction (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 );
Ceci est notre shell gestionnaire de défilement; nous avons créé quelques variables pour commencer; maintenant, prenez note de vers le bas
. Ce sera vrai si nous faisons défiler vers le bas, faux si nous faisons défiler vers le haut. Ensuite, nous réappliquons scroll_top
être la distance du haut que nous faisons défiler.
Maintenant, mettons top_item
être l'entrée la plus proche du haut de la page:
top_item = entries.filter (function (i) var distance = $ (this) .offset (). top - scroll_top; return (distance < (parent_from_top + margin) && distance > (parent_from_top - margin)); );
Ce n'est pas difficile du tout. nous utilisons simplement le filtre
méthode pour décider quelle entrée nous voulons attribuer à top_item
. Tout d'abord, nous obtenons le distance
en soustrayant le montant que nous avons fait défiler à partir du décalage supérieur de l'entrée. Alors, on retourne vrai si distance
est entre parent_from_top + margin
et parent_from_top - margin
; sinon, faux. Si cela vous rend confus, réfléchissez-y de la manière suivante: nous voulons rendre true lorsqu'un élément se trouve tout en haut de la fenêtre; dans ce cas, distance
serait égal à 0. Cependant, nous devons tenir compte du décalage supérieur du conteneur qui enveloppe nos "tweets". donc nous voulons vraiment distance
égaler parent_du_top
.
Mais il est possible que notre gestionnaire de défilement ne se déclenche pas lorsque nous sommes exactement sur ce pixel, mais plutôt lorsque nous en sommes proches. J'ai découvert cela en enregistrant la distance et j'ai constaté qu'il s'agissait de valeurs distantes d'un demi-pixel; de plus, si votre fonction de gestion de défilement n'est pas trop efficace (ce que celui-ci ne sera pas; pas encore), il est possible qu'elle ne se déclenche pas chaque événement de défilement. Pour vous assurer que vous en avez un dans la bonne zone, nous ajoutons ou soustrayons une marge pour nous donner une petite plage de travail au sein de.
Maintenant que nous avons le premier élément, faisons quelque chose avec.
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 ("visibilité", "caché"); 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 ("visibilité", "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 ("visibilité", "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 ("visibilité", "caché"); anti_repeater.css ('position': 'fixed', 'top': img_offset.top + 'px', 'left': img_offset.left + "px");
Vous remarquerez peut-être que le code ci-dessus est presque la même chose deux fois; rappelez-vous qu'il s'agit de la version non optimisée. Alors que je travaillais lentement sur le problème, je commençais par trouver comment faire fonctionner le défilement; Une fois que j'ai résolu ce problème, j'ai travaillé sur le défilement. Jusqu'à ce que toutes les fonctionnalités soient en place, il n'était pas immédiatement évident qu'il y aurait autant de similitudes. Rappelez-vous, nous optimiserons bientôt.
Alors, disséquons ceci. Si le premier article a un data-8088-starter
attribut, alors, vérifions si le anti-répéteur
a été mis en; c'est la variable qui pointera sur l'élément image qui sera corrigé pendant le défilement de la page. Si anti-répéteur
n'a pas été défini, alors nous allons obtenir notre enfant de top_item
entrée qui a le même sélecteur que inviolable
(non, ce n'est pas une façon intelligente de le faire; nous l'améliorerons plus tard). Ensuite, nous clonons cela et l’ajoutons au corps. Nous allons cacher celui-là, puis placer le cloné exactement où il devrait aller.
Si l'élément n'a pas de data-8088-starter
attribut, nous allons vérifier pour un data-8088-ender
attribut. Si cela est là, nous allons trouver le bon enfant et le rendre visible, puis retirons le anti-répéteur
et définir cette variable à nul
.
Heureusement, si nous ne baissons pas (si nous montons), c'est exactement l'inverse pour nos deux attributs. Et si le top_item
ne possède ni l'un ni l'autre des attributs, nous sommes quelque part au milieu d'une série et nous n'avons rien à changer.
Eh bien, ce code fait ce que nous voulons; cependant, si vous essayez, vous remarquerez que vous devez faire défiler très lentement pour que cela fonctionne correctement. Essayez d'ajouter les lignes console.profile ("scroll")
et console.profileEnd ()
comme les première et dernière lignes de la fonction de traitement de défilement. Pour moi, le gestionnaire prend entre 2,5 ms et 4 ms, avec 166 à 170 appels de fonction en cours.
C'est beaucoup trop long pour un gestionnaire de parchemins; et, comme vous pouvez l’imaginer, j’utilise cette machine sur un ordinateur relativement bien équipé. Notez que certaines fonctions sont appelées 30 à 31 fois; nous avons 30 entrées avec lesquelles nous travaillons, donc cela fait probablement partie de la boucle sur elles pour trouver la première. Cela signifie que plus nous avons d'entrées, plus cela fonctionnera lentement. tellement inefficace! Donc, nous devons voir comment nous pouvons améliorer cela.
Si vous pensez que jQuery est le principal coupable ici, vous avez raison. Bien que les frameworks tels que jQuery soient extrêmement utiles et facilitent le travail avec les DOM, ils sont assortis d'un compromis: les performances. Oui, ils vont toujours mieux; et oui, les navigateurs aussi. Cependant, notre situation demande le code le plus rapide possible et, dans notre cas, nous allons devoir renoncer à un peu de jQuery pour un travail direct dans le DOM qui n’est pas trop difficile.
Voyons avec la partie évidente: ce que nous faisons avec le top_item
une fois que nous l'avons trouvé. Actuellement, top_item
est un objet jQuery; Cependant, tout ce que nous faisons avec jQuery avec top_item
est trivialement plus difficile sans jQuery, nous allons donc le faire? Raw.? Lorsque nous examinons l'obtention de top_item
, nous ferons en sorte qu'il s'agisse d'un élément DOM brut.
Alors, voici ce que nous pouvons changer pour le rendre plus rapide:
obtenirAttribuer
méthode, au lieu de jQuery attr
.inviolable
qui correspond à la top_item
entrée, au lieu d'utiliser unscrollable.selector
.clodeNode
et appendChild
méthodes, au lieu des versions jQuery.style
propriété au lieu de jQuery css
méthode.removeNode
au lieu de jQuery retirer
.En appliquant ces idées, nous nous retrouvons avec ceci:
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 = "caché"; style = anti_repeater.style; style.position = 'fixe'; 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;
C'est un code bien meilleur: non seulement il supprime cette duplication, mais il n'utilise absolument aucun jQuery. (Pendant que j'écris ceci, je pense qu'il pourrait même être un peu plus rapide d'appliquer une classe CSS pour faire le style; vous pouvez expérimenter avec ça!)
Vous pourriez penser que c'est à peu près aussi bon que nous pouvons obtenir; Cependant, il y a une optimisation sérieuse qui peut avoir lieu dans l'obtention de top_item
. Actuellement, nous utilisons jQuery filtre
méthode. Si vous y réfléchissez, c'est incroyablement pauvre. Nous savons que nous allons seulement récupérer un élément de ce filtrage. mais le filtre
function ne le sait pas, alors il continue à exécuter des éléments dans notre fonction après avoir trouvé celui que nous voulons. Nous avons 30 éléments dans les entrées
, c'est donc une énorme perte de temps. Ce que nous voulons faire est la suivante:
pour (i = 0; entrées [i]; i ++) distance = $ (entrées [i]). offset (). top - scroll_top; si (distance < (parent_from_top + margin) && distance > (parent_from_top - margin)) top_item = entries [i]; Pause;
(Alternativement, nous pourrions utiliser une boucle while, avec la condition !top_item
; de toute façon, peu importe.)
De cette façon, une fois que nous trouvons le top_item
, nous pouvons arrêter de chercher. Cependant, nous pouvons faire mieux. parce que le défilement est une chose linéaire, nous pouvons prédire quel élément sera le plus proche du prochain. Si nous faisons défiler l'écran vers le bas, ce sera soit le même article que la dernière fois, soit celui qui le suit. Si nous faisons défiler vers le haut, ce sera le même élément que la dernière fois, ou l'élément précédent. Cela sera utile plus en bas de la page, car la boucle for commence toujours en haut de la page..
Alors, comment pouvons-nous mettre en œuvre cela? Eh bien, nous devons commencer par garder une trace de ce que le top_item
était lors de notre précédente exécution du gestionnaire de défilement. Nous pouvons le faire en ajoutant
prev_item = top_item;
à la toute fin de la fonction de traitement de défilement. Maintenant, là où nous trouvions précédemment le top_item
, mettre ceci:
if (prev_item) prev_item = $ (prev_item); hauteur = hauteur || 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; entrées [i]; i ++) distance = $ (entrées [i]). offset (). top - scroll_top; si (distance < (parent_from_top + margin) && distance > (parent_from_top - margin)) top_item = entries [i]; Pause;
C'est définitivement une amélioration. s'il y avait un prev_item
, alors nous pouvons l'utiliser pour trouver la prochaine top_item
. Les calculs deviennent un peu compliqués, mais voici ce que nous faisons:
prev_item
+ 1 pour trouver le bon élément dans les entrées
).top_item
, nous utilisons notre boucle for comme solution de secours.Il y a quelques autres points à aborder ici. Tout d'abord, qu'est-ce que c'est la taille
variable? Eh bien, si toutes nos entrées ont la même hauteur, nous pouvons éviter de calculer la hauteur à chaque fois dans le gestionnaire de défilement en le faisant dans la configuration. Donc, j'ai ajouté ceci à la configuration:
height = entries.outerHeight (); if (entries.length! == entries.filter (function () return $ (this) .outerHeight () === height;). length) height = null;
jQuery's outerHeight
La méthode obtient la hauteur d’un élément, y compris son remplissage et ses bordures. Si tous les éléments ont la même hauteur que le premier, alors la taille
sera réglé; autrement la taille
sera nul. Puis, quand on top_item
, on peut utiliser la taille
si c'est réglé.
L'autre chose à noter est la suivante: vous pourriez penser qu'il est inefficace de le faire prev_item.offset (). top
deux fois; cela ne devrait-il pas aller dans une variable. En fait, nous ne le ferons qu’une fois, car la deuxième instruction if ne sera appelée que si vers le bas
c'est faux; comme nous utilisons l'opérateur logique AND, les deuxièmes parties des deux instructions if ne seront jamais exécutées toutes les deux sur le même appel de fonction. Bien sûr, vous pouvez le mettre dans une variable si vous pensez que cela garde votre code plus propre, mais cela ne fait rien pour la performance..
Cependant, je ne suis toujours pas satisfait. Bien sûr, notre gestionnaire de défilement est plus rapide maintenant, mais nous pouvons le rendre meilleur. Nous n'utilisons jQuery que pour deux choses: pour obtenir le outerHeight
de prev_item
et pour obtenir le décalage supérieur de prev_item
. Pour quelque chose d'aussi petit, c'est assez cher de faire un objet jQuery. Cependant, il n'y a pas de solution immédiatement apparente, non jQuery, comme auparavant. Alors, plongeons-vous dans le code source de jQuery pour voir exactement ce que fait jQuery; De cette façon, nous pouvons extraire les bits dont nous avons besoin sans utiliser le poids coûteux de jQuery lui-même..
Commençons par le problème du décalage supérieur. Voici le code jQuery pour le décalage
méthode:
fonction (options) var elem = this [0]; if (! elem ||! elem.ownerDocument) return null; if (options) retour 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;
Cela a l'air compliqué, mais ce n'est pas si grave; nous n'avons besoin que du décalage supérieur, pas du décalage gauche, pour pouvoir le retirer et l'utiliser pour créer notre propre fonction:
fonction get_top_offset (elem) var doc = elem.ownerDocument, body = doc.body, docElem = doc.documentElement; Renvoie elem.getBoundingClientRect (). top + (self.pageYOffset || jQuery.support.boxModel && docElem.scrollTop || body.scrollTop) - docElem.clientTop || body.clientTop || 0;
Tout ce que nous avons à faire est de passer dans l'élément DOM brut et nous allons récupérer le décalage supérieur; génial! afin que nous puissions remplacer tous nos offset (). top
appelle avec cette fonction.
Que diriez-vous du outerHeight
? Celui-ci est un peu plus compliqué, car il utilise l'une de ces fonctions internes jQuery à usages multiples.
fonction (marge) retourne ceci [0]? jQuery.css (this [0], type, false, margin? "margin": "border"): null;
Comme nous pouvons le voir, cela ne fait en fait qu’un appel au css
fonction, avec ces paramètres: l'élément,? height?,? border?, et faux
. Voici à quoi ça ressemble:
fonction (elem, name, force, extra) if (name === "width" || name === "height") var val, props = cssShow, which = name === "width"? cssWidth: cssHeight; fonction getWH () val = nom === "largeur"? 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 === "margin ") val + = parseFloat (jQuery.curCSS (elem," marge "+ ceci, true)) || 0; else val - = parseFloat (jQuery.curCSS (elem," bordure "+ ce +" largeur " , true)) || 0;); if (elem.offsetWidth! == 0) getWH (); else jQuery.swap (elem, props, getWH); return Math.max (0, Math.round (val)); return jQuery.curCSS (elem, name, force);
Nous pourrions sembler désespérément perdus à ce stade, mais ça vaut la peine de suivre la logique cependant? parce que la solution est géniale! Il s'avère que nous pouvons simplement utiliser un élément offsetHeight
propriété; cela nous donne exactement ce que nous voulons!
Par conséquent, notre code ressemble maintenant à ceci:
if (prev_item) hauteur = hauteur || 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; entrées [i]; i ++) distance = get_top_offset (entrées [i]) - scroll_top; si (distance < (parent_from_top + margin) && distance > (parent_from_top - margin)) top_item = entries [i]; Pause;
Maintenant, rien ne doit être un objet jQuery! Et si vous le ralentissez, vous devriez être bien