Back to site
Since 2004, our University project has become the Internet's most widespread web hosting directory. Here we like to talk a lot about web development, networking and server security. It is, after all, our expertise. To make things better we've launched this science section with the free access to educational resources and important scientific material translated to different languages.

Poboljšana skripta za animirano skrolovanje za linkove na istoj strani

Nakon objavljivanja poslednjeg unosa o animiranom skrolovanju sa jQuery 1.2 , shvatio sam da sam izostavio važan deo koda. Zapravo, nisam ga otkrio sve dok me neko nije obavestio da je jedna druga stranica na sajtu pokvarena. Možete li da uočite problem(e)? [Napomena: problem nije u liniji 3. Marker sintakse jednostavno ne može da izađe na kraj sa regularnim izrazom sa dve kose crte u njemu ("//") i pogrešno ih tretira kao komentar.] Vidite odgovor ispod koda.

JavaScript:
  1. $(document).ready(function(){
  2.   $('a[href*=#]').click(function() {
  3.     if (location.pathname.replace(/^\//,'') == this.pathname.replace(/^\//,'')
  4.     && location.hostname == this.hostname) {
  5.       var $target = $(this.hash);
  6.       $target = $target.length && $target
  7.       || $('[name=' + this.hash.slice(1) +']');
  8.       if ($target.length) {
  9.         var targetOffset = $target.offset().top;
  10.         $('html,body')
  11.         .animate({scrollTop: targetOffset}, 1000);
  12.        return false;
  13.       }
  14.     }
  15.   });
  16. });

Odgovor: Skripta za animirano skrolovanje otima linkove koji izgledaju ovako: <a href="#">. Nekoliko ljudi potvrdilo je u komentarima da je potrebno malo više poraditi na toj skripti, pa sam mislio da bismo mogli još jednom da se okušamo.

Uzgred, iako smo priložili klik rukovalac događajem uz sve linkove koji imaju simbol "#" bilo gde u href, već sledeća linija obezbeđuje da link pokazuje na istu stranicu - proverom za podudaranje između location.pathname i this.pathname - a linija posle obezbeđuje da pokazuje na isti domen, proverom za podudaranje između location.hostname i this.hostname. Sa ovim pristupom možemo da smestimo linkove na istoj stranici bilo da oni uključuju potpuno kvalifikovani URL, relativni URL, ili samo fragment identifikator.

Proverite hash

Hajde da rešimo problem sa <a href="#"> linkovima. Prvo moramo da vidimo da li zaista ima nečega posle simbola "#" href. Očigledno, ako stoji go simbol “#”, bez ikakvih sledećih karaktera, Firefox i Internet Explorer ne smatraju ga hash-om. Safari ga, međutim, smatra. Dakle, da bismo izbegli lažnu pozitivnu na <a href="#">,prvo moramo da skinemo “#” a zatim da proverimo da li je nešto ostalo. To možemo da uradimo dodavanjem ovog uslova na prvu if naredbu: && this.hash.replace(/#/,'')

Proverite imenovano sidro

Pošto već menjamo skriptu, možda je dobar trenutak da učinimo neki njen deo čitljivijim, takođe. Od ovog dela sa logikom "kratkog spoja", koristeći && i ||, malo mi se vrti u glavi:

JavaScript:
  1. var $target = $(this.hash);
  2. $target = $target.length && $target
  3. || $('[name=' + this.hash.slice(1) +']');
  4. if ($target.length) {

Nema apsolutno ničeg lošeg u vezi s ovom sintaksom. Štaviše, napredniji JavaScripteri sve vreme je koriste. Ali osećam se prijatnije kada koristim neki jednostavniji, direktniji stil. Dakle, hajde da podesimo dve promenljive - jednu za ciljni ID i jednu za ciljno imenovano sidro. Zatim ćemo koristiti uslovne (tzv. ternarne) operatore da podesimo treću, $target, promenljivu kao ciljni ID ako ga ima, a ako ne, ciljno imenovano sidro ako postoji, a ako ne, false. Onda samo možemo da proverimo da li $target ima neku vrednost (osim false).

JavaScript:
  1. var $targetId = $(this.hash),
  2.   $targetAnchor = $('[name=' + this.hash.slice(1) +']');
  3. var $target = $targetId.length ? $targetId
  4.   : $targetAnchor.length ? $targetAnchor
  5.     : false;
  6. if ($target) {

Sada izgleda da će ponašanje animirano skrolovanje biti priključen na sve linkove iste stranice i da neće prekidati druge stvari na stranici.

Prvo petlja, na kraju vezivanje

Ali postoji još jedan problem. Budući da još uvek vezujemo .click() metod za svaki link koji sadrći "#", čak i ako na odgovarajući način izbegava da primeni animaciju na neke od tih linkova, jQuery i dalje otima linkove koji imaju umetnuti onclick rukovalac (ali, neobično, samo prvi put kada se klikne na te linkove). Da bismo rešili ovaj problem, možemo zameniti .click() sa .each(). Onda ćemo ponoviti to kroz sve linkove koji imaju "#" negde u sebi, ali postavite uslove unutar petlje, tako da vezujemo klik rukovalac tek nakon što smo izbacili sve linkove koji ne odgovaraju. Evo kako skripta izgleda sa tom promenom:

JavaScript:
  1. $(document).ready(function() {
  2.   $('a[href*=#]').each(function() {
  3.     if (location.pathname.replace(/^\//,'') == this.pathname.replace(/^\//,'')
  4.     && location.hostname == this.hostname
  5.     && this.hash.replace(/#/,'') ) {
  6.       var $targetId = $(this.hash), $targetAnchor = $('[name=' + this.hash.slice(1) +']');
  7.       var $target = $targetId.length ? $targetId : $targetAnchor.length ? $targetAnchor : false;
  8.        if ($target) {
  9.          var targetOffset = $target.offset().top;
  10.          $(this).click(function() {
  11.            $('html, body').animate({scrollTop: targetOffset}, 400);
  12.            return false;
  13.          });
  14.       }
  15.     }
  16.   });
  17. });

Obratite pažnju naročito na liniju 2 i liniju 10. Ova promena ne samo što rešava naš problem, već nekako i deluje čistije. Da li je efikasnija? Ne znam. Možda neko drugi može da nam kaže u komentarima.

Normalizujte indekse direktorijuma

Da bismo bili kompletni, verovatno bi trebalo da se pobrinemo za još jednu stvar: mogućnost da bi, na "index" stranici, link mogao da usmerava na “/path/index.htm" kada trenutna lokacija kaže "/path/" ili obrnuto. Jedan od načina da "normalizujemo" ove indeks stranice i linkove jeste da se doda još nekoliko. replace() metode na obe strane jednačine u liniji 3.

Ažurirajte

Aman je predložio u komentaru ispod da učinim da ovaj proces bude DRYer, a kangax je pružio odličan primer. Dakle, možemo da napišemo filter funkciju i da je primenimo na obe strane, umesto da ponavljamo tri zamenjivanja na svakoj strani:

JavaScript:
  1. function filterPath(string) {
  2.   return string
  3.     .replace(/^\//,'')  
  4.     .replace(/(index|default).[a-zA-Z]{3,4}$/,'')  // first additional replace
  5.     .replace(/\/$/,'');  // second additional replace
  6. }

Prvi dodatni .replace() naći će string predstavljen sa "index" ili "default", praćen tačkom, a zatim sa bilo koja tri ili četiri slova na kraju imena putanje, i zameniće ga praznim stringom (tj. ukloniće ga). Drugi će zameniti poslednju kosu crtu praznim stringom. Kao i sa lančanim jQuery metodama, ove metode regularnog izraza mogu da se postave na zasebne linije radi poboljšanja čitljivosti. Konačno, imamo neprobojne (nadam se) skripte za animirano skrolovanje za linkove na istoj stranici:

JavaScript:
  1. $(document).ready(function() {
  2.   function filterPath(string) {
  3.     return string
  4.       .replace(/^\//,'')  
  5.       .replace(/(index|default).[a-zA-Z]{3,4}$/,'')  
  6.       .replace(/\/$/,'');
  7.   }
  8.   $('a[href*=#]').each(function() {
  9.     if ( filterPath(location.pathname) == filterPath(this.pathname)
  10.     && location.hostname == this.hostname
  11.     && this.hash.replace(/#/,'') ) {
  12.       var $targetId = $(this.hash), $targetAnchor = $('[name=' + this.hash.slice(1) +']');
  13.       var $target = $targetId.length ? $targetId : $targetAnchor.length ? $targetAnchor : false;
  14.        if ($target) {
  15.          var targetOffset = $target.offset().top;
  16.          $(this).click(function() {
  17.            $('html, body').animate({scrollTop: targetOffset}, 400);
  18.            return false;
  19.          });
  20.       }
  21.     }
  22.   });
  23. });

Ako je isprobate, javite mi kako je prošlo.

Update 2

Ariel Flesler napisao je odličan ScrollTo plugin , za koji kaže da ga je inspirisao ovaj blog. Obavezno čekirajte demo .

Update 3

Neko mi je skrenuo pažnju na problem koji ova skripta ima u IE i Operi. Ne razumem kako mi je to moglo promaći, jer sam siguran da sam je testirao u oba ta pretraživača. Ali nema veze, napravio sam mali patch:

JavaScript:
  1. $(document).ready(function() {
  2.   function filterPath(string) {
  3.   return string
  4.     .replace(/^\//,'')
  5.     .replace(/(index|default).[a-zA-Z]{3,4}$/,'')
  6.     .replace(/\/$/,'');
  7.   }
  8.   var locationPath = filterPath(location.pathname);
  9.   $('a[href*=#]').each(function() {
  10.     var thisPath = filterPath(this.pathname) || locationPath;
  11.     if (  locationPath == thisPath
  12.     && (location.hostname == this.hostname || !this.hostname)
  13.     && this.hash.replace(/#/,'') ) {
  14.       var $target = $(this.hash), target = this.hash;
  15.       if (target) {
  16.         var targetOffset = $target.offset().top;
  17.         $(this).click(function(event) {
  18.           event.preventDefault();
  19.           $('html, body').animate({scrollTop: targetOffset}, 400, function() {
  20.             location.hash = target;
  21.           });
  22.         });
  23.       }
  24.     }
  25.   });
  26. });

Očigledno, IE ne vidi hostname ili pathname ako je linkov href atribut podešen sa JavaScript i sadrži samo hash (kao što je "#primer"). Dakle, sada tražim ili podudaranje ili odsustvo hostname i pathname.

Nadam se da će ova promena rešiti problem koji je Mike imao. Izgleda da radi sada u mojim testovima. A i iskoristio sam priliku da malo poboljšam kod. Sada ima blagu back dugme podršku: iako kliktanje na back dugme ne proizvodi animirano skrolovanje, barem vas vraća na prethodnu lokaciju.

Update 4

Ariel Flesler predložio je da problem koji je nekoliko ljudi već pomenulo u vezi s ovom skriptom sa Operom ima veze sa ovom linijom: $('html, body').animate({scrollTop: targetOffset}, 400); Evo popravke:

JavaScript:
  1. $(document).ready(function() {
  2.   function filterPath(string) {
  3.   return string
  4.     .replace(/^\//,'')
  5.     .replace(/(index|default).[a-zA-Z]{3,4}$/,'')
  6.     .replace(/\/$/,'');
  7.   }
  8.   var locationPath = filterPath(location.pathname);
  9.   var scrollElem = scrollableElement('html', 'body');
  10.  
  11.   $('a[href*=#]').each(function() {
  12.     var thisPath = filterPath(this.pathname) || locationPath;
  13.     if (  locationPath == thisPath
  14.     && (location.hostname == this.hostname || !this.hostname)
  15.     && this.hash.replace(/#/,'') ) {
  16.       var $target = $(this.hash), target = this.hash;
  17.       if (target) {
  18.         var targetOffset = $target.offset().top;
  19.         $(this).click(function(event) {
  20.           event.preventDefault();
  21.           $(scrollElem).animate({scrollTop: targetOffset}, 400, function() {
  22.             location.hash = target;
  23.           });
  24.         });
  25.       }
  26.     }
  27.   });
  28.  
  29.   // use the first element that is "scrollable"
  30.   function scrollableElement(els) {
  31.     for (var i = 0, argLength = arguments.length; i <argLength; i++) {
  32.       var el = arguments[i],
  33.           $scrollElement = $(el);
  34.       if ($scrollElement.scrollTop()> 0) {
  35.         return el;
  36.       } else {
  37.         $scrollElement.scrollTop(1);
  38.         var isScrollable = $scrollElement.scrollTop()> 0;
  39.         $scrollElement.scrollTop(0);
  40.         if (isScrollable) {
  41.           return el;
  42.         }
  43.       }
  44.     }
  45.     return [];
  46.   }
  47.  
  48. });

Sastavio sam mali smooth-scroll jQuery plugin sa nekoliko opcija u slučaju da je neko zainteresovan. Za robusniji skup funkcija, proverite scrollTo plugin Ariela Flesler-a.





Published (Last edited): 07-04-2013 , source: http://www.learningjquery.com/2007/10/improved-animated-scrolling-script-for-same-page-links