Themen

Nicht nur der Community Manager – auch der normale Google+ oder Facebook Nutzer steht vor folgendem Problem: muss ich den Co-Kommentator eines Posts wirklich ernst nehmen? Ist mir der Kommentator „UserXYZ“ nicht bereits zuvor schon einmal unangenehm aufgefallen, handelt es sich bei ihm vielleicht sogar um einen „Troll“? Oder ist mir der Kommentator evtl. durch einen besonders passenden oder geistreichen Kommentar positiv in Erinnerung geblieben?

Leider existiert weder in Google+ noch in FB die Möglichkeit, Freunde oder Kommentatoren mit Notizen zu versehen, so dass ich mich immer wieder in sinnlosen Scheindiskussionen mit einem Troll wiedergefunden habe. Um das zu verhindern habe ich eine Chrome-Extension programmiert, mit deren Hilfe ich jeden Kommentator eine Posts durch einen einfachen Klick klassifizieren kann:

Die Klassifikationslemente in Facebook

Die Klassifikationslemente in Facebook

Die Klassifikationslemente in Google+

Die Klassifikationslemente in Google+

Neben dem Namen eines Kommentators wird dazu ein kurzer Klassifikations-Text angezeigt. Durch wiederholte Klicks auf diesen Text kann er die Werte –, -, +/-, +, ++ annehmen. Damit kann man auf einfache und schnelle Weise fünf Klassifikationen bestimmen, die im Anschluss automatisch angezeigt werden, sobald der Name des entsprechenden Kommentators im Stream bzw. auf der Pinnwand auftaucht.

Die beiden Chrome-Extensions namens „FacebookTrolly“ bzw. „GoogleTrolly“ für Facebook und Google + finden sich hier:

Facebook

Google +

Einfach die Dateien herunterladen, und aus dem Windows-Explorer oder Mac-Finder in den Chrome Browser ziehen, den Installationsinstruktionen folgen, und der Klassifikation von Kommentatoren steht nichts mehr im Wege.

Und wer die Funktionalität nur nutzen will, kann jetzt aufhören zu lesen. Wer jedoch wissen will, wie diese Extensions funktionieren, weil er sie z.B: für Mozilla oder Safari umschreiben möchte, ist herzlich eingeladen, weiter zu lesen.

DOM Model

Die Extension basiert auf der Manipulation des DOM-Models einer in den Chrome-Browser geladenen Google +/Facebook Seite. Das DOM-Model ist die programmatische Repräsentation aller Elemente einer HTML-Seite, und mit ihm kann man in JavaScript eine HTML-Seite sowohl untersuchen als auch manipulieren.
Grob unternimmt das Trolly-Script die folgenden Schritte, nachdem eine Seite in den Browser geladen wurde:

  1. Ermittle alle Anchor-Elemente () auf der Seite, die einem Kommentator-Namen entsprechen.
  2. Durchlaufe alle gefundenen Elemente und ermittle die darin enthaltene Google+/Facebook ID des Kommentators.
  3. Finde heraus, ob bereits ein Klassifikationstext für den aktuellen Kommentator auf der Seite existiert. Falls ja, lösche das bestehende Klassifikationselement (es wird später erneut eingefügt).
  4. Suche im lokalen Speicherbereich (localStorage) des Browsers nach einer bereits existierenden Klassifikation des Users. Falls es noch keine Klassifikation gibt, verwende „+/-“.
  5. Erzeuge via DOM einen neuen Anchor-Knoten und füge diesen an der richtigen Stelle hinter dem Kommentatornamen ein.
  6. Starte zu guter Letzt einen Timer, der in 5 Sekunden die Seite bzw. alle Kommentatoren erneut aktualisiert, um so mit den Aktualisierungen der Facebook/Google+ Seiteschritt zu halten.

Anchor Elemente ermitteln

Leider bietet JavaScript standardmäßig keine Funktionen um das DOM-Model mit Hilfe eines einfachen Funktionsaufrufs nach Elementen einer bestimmten Klasse (class) zu durchsuchen. Es existieren zwar Funktionen wie document.getElementsByTagName() mit denen man beispielsweise alle Anchor () Elemente eines HTML Dokumentes ermitteln kann. Diese Elemente müssen aber anschließend genauer untersucht werden, weil nicht jedes Anchor-Element einem Kommentatornamen entspricht. Nur Anchors der Klasse yn bzw actorName kommen für unsere Zwecke in Frage:

Reverse-Engineering von Facebook-Seiten mit der Element-Untersuchen Funktion von Chrome

Reverse-Engineering von Facebook-Seiten mit der Element-Untersuchen Funktion von Chrome

Reverse-Engineering von Google+-Seiten mit der Element-Untersuchen Funktion von Chrome

Reverse-Engineering von Google+-Seiten mit der Element-Untersuchen Funktion von Chrome

Benötigt wird also eine Funktion, mit deren Hilfe man alle Anchors einer bestimmten Klasse ermitteln kann. In Google Plus werden Kommentator Namen als <a href=“….“ class=“yn ….“>Bruno Jennrich</a> dargestellt. In Facebook ist der Name nach folgendem Muster aufgebaut: <a href=“….“ class=“actorName ….“>Bruno Jennrich</a>

Um solche Konstrukte innerhalb eines HTML Dokumentes zu finden, hat Robert Nyman bereits vor einigen Jahren eine JScript-Bibliothek entwickelt (http://www.robertnyman.com, http://robertnyman.com/2005/11/07/the-ultimate-getelementsbyclassname/), die die Suche nach HTML-Elemente einer gegebenen Klasse extrem vereinfacht:

anchors = getElementsByClassName("actorName", "a", document); //fb
anchors = getElementsByClassName("yn", "a", document); // google+

Elemente durchlaufen und bestehende Klassifikationen entfernen

Nachdem in der Variable anchors alle Anchor-Elemente aufgeführt sind, die einen Kommentatornamen enthalten, müssen diese einzeln Durchlaufen und untersucht werden. Dazu wird das Nachfolge-Element eines Anchors ermittelt (nextSibling). Sowohl in Facebook als auch in Google+ sind die Anchor-Tags des Kommentatornamens glücklicherweise in einem <Span> verschachtelt (s. Abbildung oben xxx), was den Umgang mit diesen Elementen extrem vereinfacht.

<span><a href=“...“ class=“yn ...“>Bruno Jennrich</a> hat diesen
Beitrag ursprünglich gepostet: </span>

Wenn der Anchor also keinen Nachfolger hat, oder das Nachfolge-Element aus einfachem Text besteht („hat diesen Beitrag ursprünglich gepostet:“) dann wurde unser Klassifikations-Element noch nicht eingefügt. Findet sich jedoch ein Nachfolge-Element das ein HTML-ELement (nodeType==1) repräsentiert und KEIN Text-Element ist (nodeType!=3) dann handelt es sich beim Nachfolge-Element um ein zuvor eingefügtes Klassifikationselement. Dieses wird durch removeChild aus dem Elternknoten (anchors[i].parentNode) des untersuchten Anchor-Elementes entfernt.

Hinweis: Das Löschen und anschließende erneute Einfügen der Klassifikation ist dem Umstand geschuldet, dass Änderungen am Zustand eines Klassifikationselementes nicht automatisch aktualisiert werden, sondern nur durch Einfügen eines neuen Klassifikationselementes sichtbar werden. Dazu muss das bereits vor der Änderung existierende Element jedoch entfernt werden, ansonsten läuft die Web-Seite mit Elementen über.

for (i = 0; i < anchors.length; i++) {
sibling = anchors[i].nextSibling;
if ((sibling != null) && (sibling.nodeType == 1))
anchors[i].parentNode.removeChild(sibling);

Suche den Status des Kommentators und erzeuge ein neues Klassifikationselement

Nachdem ein eventuell bestehendes Klassifikationselement entfernt wurde, wird mit der Erzeugung neuer Klassifikationselemente fortgefahren. Dazu wird zunächst der aktuelle Nachfolger des Anchor-Elementes ermittelt, und für den Fall, dass es keinen Nachfolger gibt, oder der Nachfolger ein einfaches Textelement ist (z.B. „hat diesen Beitrag ursprünglich gepostet:“) wird zunächst der Klassifikationsstatus des Kommentators ermittelt:

sibling = anchors[i].nextSibling;
if ((sibling == null) || (sibling.nodeType == 3)) {
state = 0;
var oid = "g+" + anchors[i].getAttribute("oid");
//var oid = "fb" + anchors[i].getAttribute("href");
if (localStorage[oid] != undefined) {
	state = parseInt(localStorage[oid]);
	if ((state == undefined) || isNaN(state))
		state = 0;
}

Dazu wird zunächst die eindeutige ID des Kommentators bestimmt. In Google+ ist diese im oid Attribut des Anchors enthalten, bei Facebook wird dazu der Inhalt des href-Attributes verwendet. Diese ID wird als Schlüssel für den in allen modernen neuen Browsern verfügbaren localStorage genutzt, um dort nachzuchlagen, welchen Zustand (state) der Kommentator hat. Dieser Zustand wird später genutzt, um den Klassifikationstext (–, -, +/-, +, ++) zu bestimmen.

LocalStorage
Eine der Neuerungen des HTML5 Standards trägt den Namen WebStorage (http://dev.w3.org/html5/webstorage/). Darunter werden verschiedene Technologien zur Speicherung von Nutzerdaten während und auch über den Besuch einer Seite hinaus zusammengefasst. Das neue sessionStorage Element speichert dazu Daten während der Anzeigedauer einer „Hauptseite“, das localStorage Element speichert Nutzerdaten dagegen über die Anzeigedauer einer Seite hinaus. Die Daten im localStorage stehen somit zwischen verschiedenen Besuchen einer Seite zur Verfügung. Aus Sicherheitsgründen dürfen JavaScripte nur auf die Daten zugreifen, die im Kontext der geladenen Seite erzeugt wurden. Es ist also nicht möglich, mit Hilfe von JavaScript, den LocalStorage-Inhalt einer anderen HTML-Seite auszulesen.

Klassifikationselement erstellen

Nun geht es daran, dass Klassifikationelement zu erstellen. Dazu wird das Äquivalent zu folgendem HTML Text <span><a onclick=“toggle(oid); return false;“>++</a></span> in das HTML Dokument über JavaScript Objekte injiziert:

span = document.createElement("span");
a = document.createElement('a');
style = document.createAttribute("style");
switch (state) {
case 0:
	t = document.createTextNode(" +/- ");
	style.nodeValue = "color:#808000;";
	break;
case 1:
	t = document.createTextNode(" - ");
	style.nodeValue = "color:#800000;";
	break;
case 2:
	t = document.createTextNode(" -- ");
	style.nodeValue = "color:#800000;";
	break;
case 3:
	t = document.createTextNode(" + ");
	style.nodeValue = "color:#008000;";
	break;
case 4:
	t = document.createTextNode(" ++ ");
	style.nodeValue = "color:#008000;";
	break;
}
a.setAttributeNode(style);
a.addEventListener("click", new Function("event", "toggle('" + oid + "');"), false);
onclick = document.createAttribute("onclick");
onclick.nodeValue = "return false;"
a.setAttributeNode(onclick);
a.appendChild(t);
span.appendChild(a);
if (sibling != null)
	anchors[i].parentNode.insertBefore(span, sibling);
else
	anchors[i].parentNode.appendChild(span);

Die Bildung des Span- und Anchor-Elementes geschieht sehr gradlinig: Über createElement wird das jeweilige Element erzeugt und über createAttribute wird ein style-Attribute angelegt dessen Wert in Abhängigkeit des Klassifikationsstatus über nodeValue auf eine Farbe zwischen dunklem rot, gelb oder dunklem grün gesetzt wird. Gleichzeitig Wird ein Text-Knoten via createTextNode erzeugt, der einen der Werte –, -, +/-, +, ++ enthält.

Interessant wird es beim Erzeugen den OnClick-Handlers für das Anchor-Element. Leider kann man hier nicht einfach ein OnClick-Attribut mit dem Text der aufzurufenden toggle-Funktion erzeugen, die Anhand des ID des Kommentator einfach den Klassifikationszustand weiterschaltet. Das folgende Konstrukt liefert nocht das gewünschte Ergebnis:

onclick.nodeValue = "toggle('" + oid + "');"; //keine Closure!

Denn hier wird nicht der zusammengesetzte String an jede nodeValue übergeben, sondern ein auszuwertender Ausdruck. Und da die Variable oid nach dem Durchlaufen der Schleife den Wert des letzte Kommentators enthält, führt ein Kick auf ein beliebiges Klassifikationselement dazu, dass nur der Zustand des letzten Klassifikationselementes auf der HTML-Seite verändert wird. (JavaScript legt bei dieser Art der Event-Generierung also keine sogenannte Closure an).

Um sicherzustellen, dass jeder Klassifikationsanker mit dem richtigen Event-Handler ausgestattet wird, wird das auszulösende Element daher nicht als nodeValue eines Attrributes, sondern in Form eines Function Objektes erzeugt und an den click-Handler des Anchor-Elementes zugewiesen:

a.addEventListener("click", new Function("event", "toggle('" + oid+ "');"), false);

Dadurch ist sichergestellt, dass jeder Klassifikationshandler mit einer eigenen oid-ausgerüstet ist, und beim Click auf das Element die richtige Aktion ausgeführt wird.

Zuguterletzt wird durch

onclick.nodeValue = “return false;”;

sichergestellt, dass beim Click auf das Anchor-Element nicht zu einer anderen Seite bzw. erneut auf dieselbe Seite navigiert wird.
Die letzten Zeilen des Scriptes sorgen dann nur noch dafür, dass Span und Anchor Element zusammengesetzt und in das HTML-Element eingefügt werden.
Und zum Schluss darf die Funktion zum weiterschalten des Klassifikationsstatus natürlich auch nicht fehlen:

function toggle(oid) {
	state = parseInt(localStorage[oid]) + 1;
	if ((state == undefined) || isNaN(state))
		state = 1; // von 0 weiterschalten
	if (state > 4)
		state = 0;
	localStorage[oid] = "" + state;
	update(false);
	return false;
}

Hier wird einfach der im localStorage für die gegebene oid abgespeicherte Wert in eine Integer-Variable gewandelt und um 1 inkrementiert. Dabei wird ausgenutzt, dass parseInt(undefined) + 1 wiederum undefined ergibt, und so state beim ersten Aufruf von 0 (dem Defaultwert) direkt auf 1 gesetzt werden kann (die isNan()-Abfrage ist den verschiedenen Debug-Versionen geschuldet, in denen kein numerischer Wert sondern der Klartext –, -, +/-, +, ++ im localStorage abgelegt wurde).

Nachdem der neue Wert in state berechnet wurde, wird er im localStorage abegelegt, und durch update() eine Aktualisierung aller Klassifikationen auf der HTML-Seite veranlasst. Der hier übergebene Parameter false verhindert dabei, dass erneut ein Timer gestartet wird, nachdem ansonsten das automatische Update erfolgt.

Über den Autor

Bruno Jennrich

Bruno Jennrich

Bruno Jennrich ist seit 1988 selbständiger Software Entwickler und Buchautor.

Aus dem industriellen Java Umfeld kommend entwickelt er seit 2007 vor allem Web und Facebook Anwendungen.

Autor: Bruno Jennrich 3 Posts
Aus dem industriellen Java-Umfeld kommend entwickelt er seit 2007 Web- und Facebook-Anwendungen. Seit 2010 hat sich sein Entwicklungsschwerpunkt jedoch auf die Entwicklung mobiler Systeme auf Basis von iOS und Android verlagert.
15% Rabatt auf die Tickets der AllFacebook Marketing Conference am 10.11.14 >>> Rabattcode HUTTERAFMC <<< Jetzt profitieren!
Hello. Add your message here.