Erstellen eines Instagram Hashtag Typeahead in JavaScript

Bei Tailwind besteht eine unserer Hauptfunktionen darin, Tailwind-Mitgliedern Vorschläge zu Hashtags zu geben, die beim Erstellen eines Posts verwendet werden können, damit unsere Mitglieder die Reichweite ihrer Instagram-Posts maximieren können.

Eine Möglichkeit, dies zu erreichen, besteht darin, die Liste der vorgeschlagenen Hashtags bereitzustellen, die das Mitglied beim Schreiben einer Post-Beschriftung auswählen kann. Dies sehen Sie im Bild unten.

Die verschiedenen Farben für die vorgeschlagenen Hashtags helfen dem Mitglied, die Relevanz jedes vorgeschlagenen Tags zu identifizieren.

Eine wichtige UX-Verbesserung, an der unser Design- und Engineering-Team zusammengearbeitet hat, bestand darin, Vorschläge machen zu können, sobald das Mitglied mit der Eingabe eines Hashtags beginnt. Wenn ich zum Beispiel anfange, #foo in den obigen Beitrag einzugeben, möchten wir, dass eine Liste mit Hashtag-Vorschlägen einschließlich Dingen wie #football und #footballgames angezeigt wird.

Durch Aktivieren eines Typeaheads für Mitglieder wird das Erstellen eines Posts einfacher und schneller, wenn das Mitglied entscheiden muss, welche Hashtags in der Beschriftung des Posts verwendet werden sollen.

Das Ziel

Als Mitgliedstyp möchten wir Hashtags vorschlagen, die mit den Buchstaben beginnen, die das Mitglied bisher eingegeben hat. Die Vorschläge sollten im Kontext direkt unter dem Text angezeigt werden, den das Mitglied eingibt, damit das Mitglied schnell einen auswählen und die Bearbeitung der Post-Beschriftung fortsetzen kann. Wir möchten auch Reichweitenmetriken bereitstellen, damit das Mitglied das bestmögliche Tag auswählen kann. So wird alles zusammen aussehen:

Sehen Sie es in Aktion auf tailwindapp.com

Klingt ziemlich einfach, oder? Es stellte sich heraus, dass wir einige technische Herausforderungen und Einschränkungen berücksichtigen mussten.

Lassen Sie uns die wichtigsten Herausforderungen beim Erstellen und Rendern eines Typeaheads durchgehen.

So erkennen Sie, ob ein Hashtag erstellt / bearbeitet wird

Wenn ein Mitglied gerade eine Instagram-Post-Beschriftung bearbeitet, kann eines von mehreren verschiedenen Dingen zutreffen:

1. Das Mitglied gibt möglicherweise ein neues Hashtag ein oder bearbeitet ein vorhandenes

2. Das Mitglied kann sich auf ein Hashtag konzentrieren, das Hashtag jedoch derzeit nicht bearbeiten

3. Das Mitglied bearbeitet möglicherweise die Post-Beschriftung, gibt jedoch kein Hashtag ein

In welchen Fällen möchten wir einen Typeahead anzeigen?

Wie sich herausstellt, möchten wir den Typeahead für Fall 1 anzeigen, nicht jedoch für Fall 2 und 3. Typeaheads sind leistungsstarke Komponenten, sollten jedoch nur in Fällen verwendet werden, in denen Sie wissen, dass sie für Ihr Mitglied hilfreich sind. Aus diesem Grund möchten wir unseren Typeahead nur anzeigen, wenn das Mitglied einen Hashtag eingibt.

Typeahead rendern oder nicht rendern - das ist die Frage

Um zu bestimmen, ob der Typeahead angezeigt werden soll, müssen wir den Hashtag (wir nennen ihn den activeHashtag) abrufen, den das Mitglied erstellt / bearbeitet. Dazu brauchen wir:

1. Ein Keydown-Ereignishandler, der jedes Mal ausgelöst wird, wenn das Mitglied eine Taste drückt (oder eine Tastenkombination wie Umschalt + a).

Der Event-Handler ist ziemlich unkompliziert. Es ruft die getActiveHashtag-Methode auf und gibt das resultierende Hashtag zurück, das es findet:

// Ereignishandler, wenn der Benutzer // eine Taste im Post-Editor drückt onKeyPress = event => {const content = event.target.value; const key = event.key; const caretIndex = event.target.selectionStart; return getActiveHashtag (Inhalt, Schlüssel, caretIndex)}

2. Eine Funktion namens getActiveHashtag, die die folgenden Argumente akzeptiert:

  • Inhalt - Der Inhalt der Post-Beschriftung, die das Mitglied bearbeitet. ZB Hallo #world!
  • Taste - Die Taste, die das Mitglied gedrückt hat, um dieses Keydown-Ereignis auszulösen. Wir rufen dies aus dem Ereignisobjekt ab. Wenn das Mitglied beispielsweise eine Taste drückt, ist event.key a. Als Teil des Ereignisobjekts sind weitere Informationen verfügbar, die bestimmen können, ob eine Tastenkombination gedrückt wurde (z. B. Umschalt + A). In unserem Fall ist event.key jedoch alles, was uns wichtig ist.
  • caretIndex - Um festzustellen, welchen Teil der Post-Beschriftung das Mitglied bearbeitet, müssen wir wissen, wo sich das Caret befindet. damit wir genau wissen, welchen Teil der Post-Beschriftung das Mitglied bearbeitet. ZB wenn das Caret des Mitglieds hier ist: #hello worl | , dann wäre der Caret-Index 11. Wir erhalten diese Informationen von event.target.selectionStart.

Die Funktion getActiveHashtag gibt entweder das Hashtag zurück, das das Mitglied bearbeitet (z. B. #worl), oder null, wenn das Mitglied kein Hashtag aktiv bearbeitet.

Unter Berücksichtigung dieser Argumente und dieser gewünschten Ausgabe (entweder eine Zeichenfolge, die das Hashtag ist, oder null) erstellen wir einige Pseudocode-Testfälle, anhand derer wir bestimmen können, ob ein Mitglied die Post-Beschriftung bearbeitet oder nicht! Bei Tailwind führen wir eine testgesteuerte Entwicklung mit Klebeband durch, um Fehler zu reduzieren und sicherzustellen, dass wir unsere Implementierung korrekt geplant haben, bevor wir den eigentlichen Code schreiben.

Fall 1 - Mitglied erstellt oder bearbeitet einen Hashtag:

const test = () => {// Ordne const content = 'Hallo #worl' an; const key = 'l'; const caretIndex = 11; // Zeilenende // Act const r = getActiveHashtag (Inhalt, Schlüssel, caretIndex); // Test.assert (r, `# worl`) bestätigen; };

Fall 2 - Das Caret des Mitglieds befindet sich in einem Hashtag, aber das Mitglied bearbeitet es nicht:

const test = () => {// Ordne const content = 'Hello #world' an; const key = 'ArrowLeft'; const caretIndex = 11; // Zwischen dem `l` und dem` d` // Act const r = getActiveHashtag (Inhalt, Schlüssel, caretIndex); // Assert test.assert (r, null); };

Fall 3 - Das Caret des Mitglieds konzentriert sich nicht auf einen Hashtag:

const test = () => {// Ordne const content = '#Hello worl' an; const key = 'l'; const caretIndex = 11; // Zeilenende // Act const r = getActiveHashtag (Inhalt, Schlüssel, caretIndex); // Assert test.assert (r, null); };

Nachdem wir nun wissen, welche Ergebnisse getActiveHashtag zurückgeben soll, schauen wir uns die Implementierung an:

// Schlüssel, die beim // Bearbeiten eines Hashtags nie verwendet wurden. const nonEditingKeys = ['ArrowLeft', 'ArrowRight', 'Control', 'Shift', // ... etc]; // Gibt das aktiv bearbeitete Hashtag zurück oder // null, wenn keines gefunden wird. const getActiveHashtag = (content, key, caretIndex) => {// Wenn der Benutzer eine Taste gedrückt hat, die kein Zeichen ist, // bearbeitet er einen Hashtag nicht aktiv: if (nonEditingKeys.includes (key)) {return null ;; } // Finde heraus, welches Wort oder Hashtag der Benutzer bearbeitet // anhand der Caret-Position und des Inhalts: const activeWordOrHashtag = extractActiveWordOrHashtag (content, caretIndex); // Wenn das Wort, das der Benutzer bearbeitet, ein Hashtag ist, geben Sie es zurück. // Andernfalls wird null zurückgegeben. return activeWordOrHashtag [0] === '#'? activeWordOrHashtag: null; };

Sie werden feststellen, dass wir eine unterstützende Funktion haben, extractActiveWordOrHashtag, die dafür verantwortlich ist, das Wort oder Hashtag, das das Mitglied bearbeitet, aus der Post-Beschriftung abzurufen. So sieht es aus:

// Regex-Muster, das mit einem Wort oder einem Hashtag übereinstimmt. // Teste es hier aus: [https://regex101.com/r/0Bl07o/2 lightboxes(https://regex101.com/r/0Bl07o/2) const hashtagOrWordRegex = /#*\w.*/g; // Ruft das Wort ab, auf dem das Caret des Benutzers positioniert ist. const extractActiveWordOrHashtag = (content, caretIndex) => {// Gehen Sie zuerst zurück, bis wir ein Zeichen finden, das // nicht Teil eines Wortes oder Hashtags sein kann. let index = caretIndex; let Zeichen = Inhalt [Index]; do {let match = char.match (hashtagOrWordRegex); // Wenn dieses Zeichen nicht Teil eines Hashtags ist (z. B. // es ist ein Leerzeichen oder ein Punkt), geben Sie das Wort oder // Hashtag davor zurück. if (! entspricht ||! entspricht.länge) {Inhalt zurückgeben .slice (Index + 1, Inhalt.Länge) .match (hashtagOrWordRegex) [0]; } // Andernfalls gehe zum vorherigen Zeichenindex - = 1} while (Index> 0)}

Rendern des Typeaheads mit React

Dank unseres Keydown-Handlers und getActiveHashtag ist die Logik vorhanden, ob der Typeahead gerendert werden soll oder nicht. In unserem Keydown-Handler geben wir das Ergebnis von getActiveHashtag zurück. Wenn dieses Ergebnis nicht null ist, wissen wir, dass wir den Typeahead rendern müssen. So können wir activeHashtag als Requisite an unsere HashtagTypeahead-Komponente übergeben und in der Rendermethode wie folgt verwenden:

Klasse HashtagTypeahead erweitert Component {... render () {// Wenn der Benutzer ein Hashtag bearbeitet, // rendert er den Typeahead, um dem // Benutzer Vorschläge für zu verwendende Hashtags zu geben, wenn (this.props.activeHashtag) {return ( )} // Andernfalls nichts zurückgeben return null; }}

Ich habe einige Details der React-Implementierung abstrahiert, da die Art und Weise, wie dies durchgeführt wird, von der spezifischen Typeahead-Bibliothek abhängt, die verwendet wird. Wenn Sie eine Typeahead-Bibliothek auswählen und implementieren, kann dies leicht ein eigener Blog-Beitrag sein! Bei Tailwind verwenden wir React ausgiebig und verfügen über einige intern erstellte Komponenten, die für uns typeaheads sind. Einige ausgezeichnete Standardkomponenten von Typeahead sind React Bootstrap Typeahead oder React Autosuggest. Dies könnte Ihnen die Zeit und Energie ersparen, Ihren eigenen Typeahead zu bauen.

Dies ist mein erstes Projekt seit Beginn bei Tailwind und ich hatte eine Menge Spaß daran zu arbeiten. Wenn Sie interessante Frontend- und Backend-Probleme lösen möchten, stellen wir ein! Wenn Sie Fragen zu dieser Geschichte oder zu Rückenwind haben, können Sie mich unter [email protected] erreichen.

Ursprünglich veröffentlicht am 19. April 2019 unter https://martyjon.es.