Browser Extension Aranjați mesaje Tumblr în ordine cronologică

Crearea de extensii de browser mici este o modalitate foarte bună de a învăța JavaScript. Nu aveți nevoie să vă faceți griji cu privire la compatibilitatea cu browser-ul încrucișat, puteți face proiectul cât mai mic sau mai mare pe cât vă place și puteți rezolva problemele pe site-urile celorlalți care vă deranjează în mod special. În acest post, vom crea un script scurt care rearanjează automat paginile arhivelor Tumblr pentru a le citi în ordine cronologică - și vom afla despre NodeList pe drum.


Problema

În această săptămână, Ryan North a început un nou Tumblr, în care trece prin noutățile primului film Înapoi în viitor, câte o pagină în același timp. Acesta este un lucru pe care doresc să îl citesc.

Faceți clic pentru a citi.

Cu toate acestea, Tumblr își afișează posturile în ordine inversă cronologică - adică cu cele mai recente postări. Acest lucru are sens pentru o mulțime de site-uri de pe Tumblr, dar în acest caz, chiar vreau să le citesc în ordinea în care au fost scrise. Structura actuală a site-ului înseamnă că, pentru a face acest lucru, trebuie să derulăm în partea de sus a primului post (scris în primul rând), să derulăm în jos pe măsură ce îl citesc, apoi să derulez în sus în partea de sus a celui de-al doilea mesaj și să derulez în jos prin asta ...

Aceasta este o mică problemă mondială, prima, lume - total banală și aproape jenantă de menționat. Dar asta nu înseamnă că nu vreau să o rezolv.

Până la sfârșitul acestui tutorial, veți face acest Tumblr - sau orice Tumblr pe care îl alegeți - afișați postările în ordinea cronologică a paginii, în mod automat.


Cum putem face asta??

Nu avem control asupra designului site-ului BtotheF; nu oferă opțiuni pentru noi, deci nu putem schimba ordinea în care sunt afișate mesajele pe partea server-ului de pagină.

Dar odată ce HTML-ul este trimis browserului, este al nostru. Putem manipula oricum dorim. Și, bineînțeles, limba în care putem face acest lucru este JavaScript.

Voi da un demo rapid. Voi folosi Chrome și vă recomand să faceți și tu - dacă nu puteți sau nu doriți să luați o copie de Firebug.

Navigați la http://btothef.tumblr.com/ și deschideți consola JavaScript (Instrumente> Consola JavaScript, în Chrome). Tip:

 alert ( "Hi");

... și apăsați Enter.

Wow! ... Bine, bine, asta nu sa dovedit prea mult. Incearca asta:

 document.body.appendChild (document.createTextNode ( "Hi"));

Aceasta va lua documentul HTML element și adăugați un nou nod text - o bucată de text, în esență - conținând cuvântul "Hi". Dacă nu ați urmat niciunul din acestea, ridicați viteza cu HTML.

Pentru a vedea rezultatul, parcurgeți în jos până la partea de jos a Tumblr-ului:

Puțin mai impresionant, de vreme ce am schimbat conținutul paginii. Noul punct de text este în partea de jos, deoarece am folosit appendChild () - care adaugă nodul specificat după orice altceva în interiorul lui . În ceea ce privește browserul nostru, ultimele câteva linii HTML arată acum:

  Bună  

Asa de! Acum avem două probleme:

  1. Cum folosim JavaScript pentru a rearanja toate postările din pagină?
  2. Cum facem să se întâmple acest lucru în mod automat, fără a fi nevoie să ne încurcăm în consola de fiecare dată când citim Tumblr-ul?

Vom aborda acestea în ordine.


Rearanjarea posturilor

În primul rând, să ne dăm seama cum să "găsim" toate postările din HTML. În Chrome, faceți clic dreapta pe undeva în postarea de sus și selectați "Inspect element". (Firebug ar trebui să adauge aceeași scurtătură pentru Firefox, pentru alte browsere, încercați pictograma lupă.)

Se va afișa fila Elemente din Instrumentele pentru dezvoltatori, cu elementul selectat evidențiat:

Pe măsură ce deplasați mouse-ul peste diferite elemente din această vizualizare HTML, le veți vedea evidențiate în pagina în sine. În cele din urmă, veți descoperi că întreaga postare este cuprinsă într-un div cu o clasă de post. După cum puteți vedea din captura de ecran de mai sus, există și alte câteva astfel div elemente, și toate acestea sunt conținute într-un master div cu un id de cuprins.

Putem selecta toate aceste elemente postate simultan cu o singură linie de cod; tastați-l în consola JavaScript:

 document.getElementById ( "conținuturi") getElementsByClassName ( "post").

(Tehnic, am putea fi scris document.getElementsByClassName ( "post"), dar cui ii pasa?)

Veți vedea - din nou, în Chrome - un feedback:

Grozav! Acest lucru indică faptul că a funcționat. Dacă faceți clic pe săgeți, veți vedea conținutul fiecăruia div.

S-ar putea să atribuim acest lucru unei variabile, pentru a ne salva de a trebui să-l tipărim din nou și peste:

 var posturi = document.getElementById ("conținut"). getElementsByClassName ("post");

(De data aceasta, consola va reveni nedefinit; puteți verifica dacă a atribuit corect variabila doar prin tastarea cuvântului posturi în consola.)

Putem trece prin toate elementele din posturi ca și cum ar fi o matrice:

 pentru (var i = 0; i < posts.length; i++)  console.log(posts[i]); 

Apăsați Shift-Enter pentru a adăuga o nouă linie la codul dvs., fără ao rula, în consola JavaScript. console.log () doar urmărește conținutul său la consola, care este mult mai puțin enervant decât un alerta() și mult mai puțin perturbator decât adăugarea de text la DOM DOM.

Rezultatul acestei bucle simple va fi doar o listă a tuturor div elemente. Rece. Probabil că putem face așa ceva ...

 var numPosts = posts.length; pentru (var i = numPosts - 1; i> = 0; i -) posts.push (posturi [i]); 

Încearcă! Veți vedea acest lucru:

 TypeError: Obiect # nu are nici o metodă "împinge"

şobolanii.

Un NodeList nu este o matrice

Când o folosim getElementsByClassName (), nu recuperăm o gamă simplă de elemente, vom prelua un NodeList. Ca o matrice, aceasta este o colecție de elemente; spre deosebire de o matrice, colecția este actualizată în timp real. Dacă DOM-ul HTML este modificat, conținutul lui posturi se modifică pentru a se potrivi.

De exemplu, să creăm un nou div cu o clasă de post și adăugați-l la cuprins div:

 var newPostDiv = document.createElement ("div"); newPostDiv.setAttribute ("clasă", "postare"); document.getElementById ( "Conținutul") appendChild (newPostDiv.);

Rulați, apoi tastați posturi și apăsați Enter. Veți vedea un extra div în listă - chiar dacă nu am atins-o posturi variabil.

Din moment ce a NodeList nu este o matrice, nu are cele mai multe dintre metodele și proprietățile lui mulțime, precum Apăsați() De fapt, am folosit deja singura proprietate: lungime. Singura metodă este .item (n), care doar devine nal celui de-al treilea element din listă.

Cu toate acestea, demonstrația noastră anterioară cu appendChild () sugerează o soluție alternativă: ce se întâmplă dacă, în loc să încerci să rearanjezi elementele din NodeList în cadrul NodeList, le-am rearanjat în cadrul paginii?

Mai întâi, vom trece prin listă în ordine inversă și vom adăuga fiecare element la rândul său cuprins div; atunci vom elimina mesajele originale.

Reîmprospătați pagina Tumblr pentru a șterge toate gunoaiele suplimentare pe care le-am adăugat DOM, apoi faceți primul pas, de a adăuga elementele în ordine inversă:

 // trebuie să definim din nou postările, deoarece am pierdut-o când am reîmprospătat var posts = document.getElementById ("contents"). getElementsByClassName ("post"); var numPosts = posts.length; pentru (var i = numPosts - 1; i> = 0; i--) document.getElementById ("conținut"). appendChild (posturi [i]); 

Bine, acum ... așteptăm, ce?

Primul post (cronologic) este deja în partea de sus, iar ultimul post este în partea de jos, fără a mai fi repetate postări. Acest lucru poate părea surprinzător, dar are sens: nu am făcut-o clona orice elemente ale NodeList, le-am adăugat doar la cuprins div, în ordine inversă. Din moment ce NodeList reflectă întotdeauna conținutul DOM HTML, noi doar mutat fiecare dintre cele 11 elemente într-o nouă poziție - al 11-lea element a fost adăugat la final (fără schimbare în poziție); apoi 10-a sa mutat după aceea; al 9-lea după aceea și așa mai departe, până când elementul 1 a avut dreptate la final.

Deci, problema a fost rezolvată, în cinci linii simple de cod. Acum, să o automatizăm.


Întoarcerea la o extensie a browserului

Trei dintre primele patru browsere vă permit să creați extensii de browser - add-on-uri instalabile - utilizând JavaScript. (Nu te va surprinde să afli că Internet Explorer este excepția.) Iată instrucțiunile pentru Chrome, instrucțiunile pentru Firefox și instrucțiunile pentru Safari.

Dar nu vom folosi niciuna dintre acestea. În schimb, vom folosi un add-on grozav numit Greasemonkey, care vă permite să instalați mici scripturi JS fără a fi nevoie să le includeți ca extensii de browser complet dezvoltate. În esență, injectează fragmente de JavaScript în pagină, odată ce este încărcat.

Chrome are deja suport pentru Greasemonkey. Opera are ceva foarte asemănător cu Greasemonkey copt în: UserJS. Pentru Firefox, va trebui să instalați extensia Greasemonkey. Pentru Safari, instalați SIMBL și apoi GreaseKit. Pentru IE, instalați IE7Pro sau Trixie.

Scrierea scriptului Greasemonkey

Creați un fișier text nou și denumiți-l ChronologicalTumblr.user.js. Asigurați-vă că eliminați valoarea implicită .txt extensie și pe care o utilizați .user.js, mai degrabă decât doar .js - aceasta este extensia pentru un script Greasemonkey.

Copiați codul nostru de rearanjare post în fișier:

 var posturi = document.getElementById ("conținut"). getElementsByClassName ("post"); var numPosts = posts.length; pentru (var i = numPosts - 1; i> = 0; i--) document.getElementById ("conținut"). appendChild (posturi [i]); 

Acum, trebuie să adăugăm câteva metadate suplimentare în partea de sus a scriptului - detalii despre script-ul însuși. Toate aceste detalii ar trebui să figureze în comentarii, începând cu == UserScript == și se încheie cu == / UserScript ==:

 // == UserScript == // // == / UserScript == var posturi = document.getElementById ("conținut") getElementsByClassName ("post"); var numPosts = posts.length; pentru (var i = numPosts - 1; i> = 0; i--) document.getElementById ("conținut"). appendChild (posturi [i]); 

Mai întâi, vom adăuga un nume și o descriere:

 // == UserScript == // @ nume Cronologic Tumblr // @description Afișează postările Tumblr în ordine cronologică // == / UserScript == var posts = document.getElementById ("contents"). GetElementsByClassName ("post"); var numPosts = posts.length; pentru (var i = numPosts - 1; i> = 0; i--) document.getElementById ("conținut"). appendChild (posturi [i]); 

Apoi, vom specifica a Spațiu de nume. Combinația dintre nume și spațiul de nume este ceea ce identifică în mod unic scriptul; dacă încercați să instalați un alt script Greasemonkey cu același nume și spațiu de nume ca un script existent, acesta va suprascrie cel vechi - dacă este numai numele sau meciul din spațiul de nume, va instala doar noul unul lângă cel vechi.

Este comun să folosiți o adresă URL aici, așa că o să folosesc funcția Activetuts +. Ar trebui să utilizați propriul dvs., deoarece sunteți în controlul acestuia:

 // == UserScript == // @ nume Tumblr cronologic // @description Afișează mesajele Tumblr în ordine cronologică // @namespace http://active.tutsplus.com/ // == / UserScript == var posts = document. getElementById ( "conținuturi") getElementsByClassName ( "post"). var numPosts = posts.length; pentru (var i = numPosts - 1; i> = 0; i--) document.getElementById ("conținut"). appendChild (posturi [i]); 

Acum trebuie să specificăm care URL-uri ar trebui să afecteze acest script - adică pe ce pagini web ar trebui automat să fie injectat JavaScript. Vreau ca acest script să funcționeze pe pagina de pornire BtotheF, plus celelalte pagini de arhivă, cum ar fi http://btothef.tumblr.com/page/2:

 // == UserScript == // @ nume Cronologic Tumblr // @description Afișează postările Tumblr în ordine cronologică // @namespace http://active.tutsplus.com/ // @include http://btothef.tumblr.com // // include http://btothef.tumblr.com/page/* // == / UserScript == var posts = document.getElementById ("conținut") getElementsByClassName ("post"); var numPosts = posts.length; pentru (var i = numPosts - 1; i> = 0; i--) document.getElementById ("conținut"). appendChild (posturi [i]); 

* este un wildcard; se asigură că scriptul va fi inserat în fiecare pagină de arhivă a BtotheF Tumblr.

Am putea folosi un wildcard pentru a afișa fiecare pagină a arhivelor Tumblr în ordine cronologică:

 // == UserScript == // @name Cronologic Tumblr // @description Afișează postările Tumblr în ordine cronologică // @namespace http://active.tutsplus.com/ // @include http: //*.tumblr.com // // include http: //*.tumblr.com/page/* // == / UserScript == var posts = document.getElementById ("conținut") getElementsByClassName ("post"); var numPosts = posts.length; pentru (var i = numPosts - 1; i> = 0; i--) document.getElementById ("conținut"). appendChild (posturi [i]); 

În acest caz, ar putea fi o idee bună să vă îmbunătățiți scriptul pentru a introduce un buton sau o legătură într-o pagină care rearanjează postările atunci când se face clic. În acest fel, îl puteți utiliza când este necesar, fără a fi nevoie să adăugați manual fiecare URL Tumblr ca un @include parametru.

Există alți parametri de metadate pe care le puteți adăuga, dar acest lucru este tot ce ne trebuie pentru moment.

Finalizarea opțiunii

Instalarea scriptului în Chrome este ușoară: trageți-o și trageți-o din unitatea hard disk într-o fereastră de browser. Vă va avertiza că scenariile ar putea fi periculoase (doar faceți clic pe Continuare) și apoi verificați din nou că doriți să instalați unul specific:

Hit Install. Dacă utilizați un alt browser, consultați manualul relevant al scriptului Greasemonkey - în orice caz, va fi simplu.

Odată instalat, îl puteți vedea în lista de extensii:

Întoarceți-vă la http://btothef.tumblr.com/, sau apăsați refresh dacă aveți încă deschis.

Mesajele se afișează automat în ordine cronologică!

Nu este perfect: este posibil să fi observat că butoanele de navigare au fost mutate în partea de sus a paginii, deoarece sunt cuprinse într-un div cu o clasă de post. Dar hei - dacă vă deranjează asta, aveți instrumentele necesare pentru ao rezolva acum.

Cod