Browser-extensie Tumblr-berichten in chronologische volgorde rangschikken

Het maken van kleine browser-extensies is een geweldige manier om JavaScript te leren. U hoeft zich geen zorgen te maken over de compatibiliteit tussen browsers, u kunt het project zo klein of groot maken als u maar wilt en u kunt problemen op de websites van andere mensen oplossen die u specifiek lastig vallen. In dit bericht maken we een kort script dat de Tumblr-archiefpagina's automatisch in chronologische volgorde herleest - en we leren over nodelist onderweg.


Het probleem

Deze week begon Ryan North aan een nieuwe Tumblr, waarin hij de eerste film Back to the Future, pagina voor pagina, doorbladert. Dit is iets dat ik wil lezen.

Klik om te lezen.

Tumblr geeft zijn berichten echter weer in omgekeerde chronologische volgorde weer, dat wil zeggen, met de meest recente berichten als eerste. Dit is logisch voor veel sites op Tumblr, maar in dit geval wil ik ze echt lezen in de volgorde waarin ze zijn geschreven. De huidige lay-out van de site betekent dat ik daarvoor naar de bovenkant van het eerste (eerste geschreven) bericht moet scrollen, naar beneden moet scrollen terwijl ik het lees en dan naar de bovenkant van het tweede bericht moet scrollen en omlaag scrollen door dat…

Dit is een klein, klein eerste wereldprobleem - totaal triviaal en bijna gênant om op te noemen. Maar dat betekent niet dat ik het niet wil repareren.

Aan het einde van deze tutorial maak je deze Tumblr - of elke Tumblr die je kiest - zijn berichten in chronologische volgorde op de pagina weergeven, automatisch.


Hoe kunnen we dit doen?

We hebben geen controle over het ontwerp van de BtotheF-site; het biedt ons geen opties, dus we kunnen de volgorde waarin de berichten op de serverpagina worden weergegeven niet veranderen.

Maar zodra de HTML naar de browser is verzonden, is die van ons. We kunnen het manipuleren zoals we het willen. En natuurlijk is de taal waarmee we dit kunnen doen JavaScript.

Ik zal een snelle demo geven. Ik zal Chrome gebruiken en raad aan dat ook te doen. Als je het niet kunt of niet wilt, pak dan een exemplaar van Firebug.

Blader naar http://btothef.tumblr.com/ en open de JavaScript-console (Hulpmiddelen> JavaScript-console, in Chrome). Type:

 alert ( "Hallo");

... en druk op Enter.

Wauw! ... Oké, dat bewees niet veel. Probeer dit:

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

Dit neemt de HTML-document's element, en voeg een nieuw tekstknooppunt toe - een stuk tekst, in principe - met het woord "Hallo". Als je dat niet hebt gedaan, kun je HTML gebruiken.

Om het resultaat te zien, scrollt u naar beneden naar de onderkant van de Tumblr:

Iets meer indrukwekkend, omdat we de inhoud van de pagina hebben veranderd. Het nieuwe tekstknooppunt staat helemaal onderaan omdat we het hebben gebruikt appendChild () - die het opgegeven knooppunt toevoegt na al het andere in de . Wat onze browser betreft, zien de laatste regels HTML er nu als volgt uit:

  Hoi  

Zo! Nu hebben we twee problemen:

  1. Hoe gebruiken we JavaScript om alle berichten op de pagina te herschikken?
  2. Hoe we dit automatisch laten gebeuren, zonder elke keer dat we de Tumblr lezen in de console te moeten rommelen?

We zullen deze in volgorde behandelen.


De posts herschikken

Laten we eerst eens kijken hoe we alle berichten in de HTML kunnen 'vinden'. Klik in Chrome met de rechtermuisknop ergens in het bovenste bericht en selecteer 'Inspecteer element'. (Firebug moet dezelfde snelkoppeling toevoegen aan Firefox, voor andere browsers, probeer het vergrootglaspictogram.)

Het tabblad Elementen van de Developer Tools verschijnt, met het geselecteerde element gemarkeerd:

Terwijl u in deze HTML-weergave over verschillende elementen zweeft, ziet u ze op de pagina zelf worden gemarkeerd. Uiteindelijk zult u merken dat het volledige bericht is opgenomen in a div met een klasse van post. Zoals u kunt zien aan de hand van de bovenstaande schermafbeelding, zijn er verschillende andere div elementen, en ze zijn allemaal vervat in een master-div met een id van inhoud.

We kunnen al deze berichtelementen in één keer selecteren met een enkele regel code; typ dit in de JavaScript-console:

 document.getElementById ( "inhoud") getElementsByClassName ( "post.");

(Technisch gezien hadden we zojuist kunnen schrijven document.getElementsByClassName ( "post"), maar wie maakt het uit?)

U ziet - nogmaals, in Chrome - wat feedback:

Super goed! Dit geeft aan dat het werkte. Als u op de pijlen klikt, ziet u de inhoud van elk div.

We kunnen dit net zo goed toewijzen aan een variabele, om te voorkomen dat we het steeds opnieuw moeten typen:

 var posts = document.getElementById ("contents"). getElementsByClassName ("post");

(Deze keer zal de console terugkeren onbepaald; u kunt controleren of de variabele correct is toegewezen door het woord gewoon in te typen posts in de console.)

We kunnen alle elementen inlopen posts alsof het een array is:

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

Druk op Shift-Enter om een ​​nieuwe regel aan uw code toe te voegen zonder deze uit te voeren, in de JavaScript-console. console.log () traceert gewoon de inhoud ervan naar de console, wat veel minder vervelend is dan een alert () en veel minder verstorend dan het toevoegen van tekst aan de HTML DOM.

Het resultaat van deze eenvoudige lus is slechts een lijst van alle div elementen. Stoer. Dus vermoedelijk kunnen we zoiets doen ...

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

Probeer het! Je ziet dit:

 TypeError: Object # heeft geen methode 'duwen'

ratten.

Een NodeList is geen array

Wanneer we gebruiken getElementsByClassName (), we halen geen eenvoudige array van elementen op, we halen een NodeList op. Net als een array is dit een verzameling elementen; in tegenstelling tot een array, wordt de verzameling in realtime bijgewerkt. Als de HTML DOM is gewijzigd, is de inhoud van posts wordt aangepast om overeen te komen.

Laten we bijvoorbeeld een nieuwe div maken met een klasse van post en voeg het toe aan de inhoud div:

 var newPostDiv = document.createElement ("div"); newPostDiv.setAttribute ("class", "post"); document.getElementById ( "inhoud") appendChild (newPostDiv).;

Voer dat uit, typ dan posts en druk op Enter. Je ziet een extra div in de lijst - ook al hebben we de posts veranderlijk.

Sinds een nodelist is geen array, het heeft niet de meeste methoden en eigenschappen van reeks, zoals Duwen() We hebben zelfs de enige eigenschap al gebruikt: lengte. De enige methode is .artikel (n), wat net de nhet item in de lijst.

Echter, onze eerdere demonstratie met appendChild () suggereert een alternatieve oplossing: wat als, in plaats van te proberen de elementen van de nodelist binnen de nodelist, we hebben ze op de pagina opnieuw gerangschikt?

Eerst zullen we de lijst in omgekeerde volgorde doorlopen en elk element toevoegen aan de inhoud div; dan zullen we de originele berichten verwijderen.

Ververs de Tumblr-pagina om al het extra afval dat we hebben toegevoegd aan de DOM te verwijderen en voer vervolgens de eerste stap uit door de elementen in omgekeerde volgorde toe te voegen:

 // moeten posts opnieuw definiëren, omdat we het verloren hebben toen we var posts = document.getElementById ("contents"). getElementsByClassName ("post") vernieuwden; var numPosts = posts.length; for (var i = numPosts - 1; i> = 0; i--) document.getElementById ("contents"). appendChild (berichten [i]); 

Oké, nu ... wachten, wat?

Het (chronologische) eerste bericht staat al bovenaan en het laatste bericht staat onderaan, zonder berichten te herhalen. Dit lijkt misschien verrassend, maar het is logisch: wij niet kloon alle elementen van de nodelist, we hebben ze zojuist aan de inhoud div, in omgekeerde volgorde. Sinds de nodelist weerspiegelt altijd de inhoud van de HTML DOM, we gewoon verhuisd elk van de 11 elementen naar een nieuwe positie - het 11e element werd toegevoegd aan het einde (geen verandering in positie); toen werd de 10e daarna bewogen; de 9e daarna, enzovoort, totdat het eerste element helemaal aan het einde was.

Dus probleem opgelost, in vijf eenvoudige regels code. Laten we het nu automatiseren.


Het omzetten in een browserextensie

In drie van de vier beste browsers kunt u browseruitbreidingen maken - installeerbare add-ons - met behulp van JavaScript. (Het zal je niet verbazen dat Internet Explorer de uitzondering is.) Dit zijn de instructies voor Chrome, de instructies voor Firefox en de instructies voor Safari.

Maar we zullen er geen van gebruiken. In plaats daarvan gebruiken we een geweldige add-on genaamd Greasemonkey, waarmee je kleine JS-scripts kunt installeren zonder ze als volwaardige browser-extensies te hoeven verpakken. In wezen worden er JavaScript-fragmenten in de pagina geladen zodra deze is geladen.

Chrome heeft al ondersteuning voor Greasemonkey ingebakken. Opera heeft iets vergelijkbaars als Greasemonkey ingebakken: UserJS. Voor Firefox moet u de Greasemonkey-extensie installeren. Voor Safari, installeer SIMBL en dan GreaseKit. Installeer voor IE, IE7Pro of Trixie.

Het Greasemonkey-script schrijven

Maak een nieuw tekstbestand en geef het een naam ChronologicalTumblr.user.js. Zorg ervoor dat u de standaard verwijdert .tekst extensie en die u gebruikt .user.js, in plaats van alleen .js - dit is de extensie voor een Greasemonkey-script.

Kopieer onze post herrangschikking code in het bestand:

 var posts = document.getElementById ("contents"). getElementsByClassName ("post"); var numPosts = posts.length; for (var i = numPosts - 1; i> = 0; i--) document.getElementById ("contents"). appendChild (berichten [i]); 

Nu moeten we wat extra metadata aan de bovenkant van het script toevoegen - details over het script zelf. Al deze details zouden in reacties moeten gaan, te beginnen met == userscript == en eindigend met == / userscript ==:

 // == UserScript == // // == / UserScript == var posts = document.getElementById ("contents"). GetElementsByClassName ("post"); getElementsByClassName ("post"); var numPosts = posts.length; for (var i = numPosts - 1; i> = 0; i--) document.getElementById ("contents"). appendChild (berichten [i]); 

Eerst voegen we een naam en een beschrijving toe:

 // == UserScript == // @name Chronological Tumblr // @description Geeft Tumblr-berichten in chronologische volgorde weer // == / UserScript == var posts = document.getElementById ("contents"). GetElementsByClassName ("post"); var numPosts = posts.length; for (var i = numPosts - 1; i> = 0; i--) document.getElementById ("contents"). appendChild (berichten [i]); 

Vervolgens geven we een namespace. De combinatie van de naam en de naamruimte is wat het script op unieke wijze identificeert; als je een ander Greasemonkey-script probeert te installeren met dezelfde naam en naamruimte als een bestaand script, het zal de oude overschrijven - als alleen de naam of de namespace-match, het zal gewoon de nieuwe naast de oude installeren.

Het is gebruikelijk om hier een URL te gebruiken, dus ik gebruik Activetuts + 's. Je zou je eigen moeten gebruiken, omdat je er controle over hebt:

 // == UserScript == // @name Chronological Tumblr // @description Geeft Tumblr-berichten in chronologische volgorde weer // @namespace http://active.tutsplus.com/ // == / UserScript == var posts = document. getElementById ( "inhoud") getElementsByClassName ( "post."); var numPosts = posts.length; for (var i = numPosts - 1; i> = 0; i--) document.getElementById ("contents"). appendChild (berichten [i]); 

Nu moeten we opgeven welke URL's dit script moet beïnvloeden, dat wil zeggen, op welke webpagina's het JavaScript automatisch moet worden geïnjecteerd. Ik wil dat dit script werkt op de BtotheF-startpagina, plus de andere archiefpagina's, zoals http://btothef.tumblr.com/page/2:

 // == UserScript == // @name Chronological Tumblr // @description Toont Tumblr-berichten in chronologische volgorde // @ namespace http://active.tutsplus.com/ // @include http://btothef.tumblr.com / // @ include http://btothef.tumblr.com/page/* // == / UserScript == var posts = document.getElementById ("contents"). getElementsByClassName ("post"); var numPosts = posts.length; for (var i = numPosts - 1; i> = 0; i--) document.getElementById ("contents"). appendChild (berichten [i]); 

De * is een wildcard; het zorgt ervoor dat het script in elke afzonderlijke archiefpagina van de BtotheF Tumblr wordt ingevoegd.

We zouden een wildcard kunnen gebruiken om alle afzonderlijke pagina's van Tumblr in chronologische volgorde weer te geven:

 // == UserScript == // @name Chronological Tumblr // @description Geeft Tumblr-berichten in chronologische volgorde weer // @namespace http://active.tutsplus.com/ // @include http: //*.tumblr.com / // @ include http: //*.tumblr.com/page/* // == / UserScript == var posts = document.getElementById ("contents"). getElementsByClassName ("post"); var numPosts = posts.length; for (var i = numPosts - 1; i> = 0; i--) document.getElementById ("contents"). appendChild (berichten [i]); 

In dit geval kan het een goed idee zijn om uw script te verbeteren om een ​​knop in te voegen of een link naar de pagina die de berichten herschikt wanneer erop wordt geklikt. Op die manier kunt u het indien nodig gebruiken, zonder dat u elke afzonderlijke Tumblr-URL handmatig hoeft toe te voegen @include parameter.

Er zijn andere metadata-parameters die u zou kunnen toevoegen, maar dit is alles wat we nu nodig hebben.

Afwerken

Het installeren van het script in Chrome is eenvoudig: sleep het vanaf uw harde schijf naar een browservenster. Het waarschuwt u dat scripts mogelijk gevaarlijk kunnen zijn (klik gewoon op Doorgaan) en controleer nogmaals of u deze specifieke wilt installeren:

Druk op Installeren. Als u een andere browser gebruikt, raadpleegt u de handleiding van het relevante Greasemonkey-script - in elk geval is het eenvoudig.

Zodra het is geïnstalleerd, kunt u het in uw lijst met extensies zien:

Ga terug naar http://btothef.tumblr.com/ of klik op Vernieuwen als je het nog steeds hebt geopend.

De berichten worden nu automatisch in chronologische volgorde weergegeven!

Het is niet perfect: je hebt misschien gemerkt dat de navigatieknoppen naar de bovenkant van de pagina zijn verplaatst, omdat ze zich in een div bevinden met een klasse van post. Maar goed, als dat jou irriteert, heb je de tools om het nu te repareren.