Bygga en Instagram Hashtag Typeahead i JavaScript

På Tailwind är en av våra kärnfunktioner att ge Tailwind-medlemmar förslag om hashtags att använda när de skapar ett inlägg, så att våra medlemmar kan maximera räckvidden för sina Instagram-inlägg.

Ett sätt att uppnå detta är genom att tillhandahålla en lista med föreslagna hashtags som medlemmen kan välja medan han skriver en posttexter - Du kan se detta på bilden nedan.

De olika färgerna för de föreslagna hashtags hjälper medlemmen att identifiera relevansen för varje föreslagen tagg.

En viktig UX-förbättring som vårt design- och konstruktionsteam samarbetade med var att kunna lämna förslag så snart medlemmen börjar skriva en hashtag. Om jag till exempel börjar skriva #foo i inlägget ovan, skulle vi till exempel vilja att en lista kommer upp som visar hashtagförslag inklusive saker som #fotboll och #fotbollspel.

Om du aktiverar en typhuvud för medlemmar kommer det att bli enklare och snabbare att skapa ett inlägg när medlemmen måste bestämma vilka hashtags att använda i postens bildtexter.

Målet

Som medlemstyper vill vi föreslå hashtags som börjar med bokstäverna som medlemmen har skrivit in hittills. Förslagen bör tas upp i sammanhang, precis under den text som medlemmen skriver så att medlemmen snabbt kan välja en och fortsätta redigera posttexten. Vi vill också tillhandahålla räckviddsstatistik så att medlemmen kan välja den bästa möjliga taggen. Så här kommer allt att se ut när det sätts ihop:

Se det i aktion på tailwindapp.com

Låter ganska enkelt, eller hur? Det visar sig att det finns flera tekniska utmaningar och begränsningar som vi var tvungna att överväga.

Låt oss gå igenom de viktigaste utmaningarna med att bygga och göra en typhuvud.

Hur man upptäcker om en hashtag skapas / redigeras

När en medlem håller på att redigera en bildtexter på Instagram, kan en av flera olika saker vara sant:

1. Medlemmen kanske skriver en ny hashtag eller redigerar en befintlig

2. Medlemmen kan vara fokuserad på en hashtag, men inte redigera faktiskt hashtaggen just nu

3. Medlemmen kan redigera posttexten men inte skriva en hashtag

I vilka fall vill vi visa en typhuvud?

Det visar sig att vi skulle vilja visa typhuvudet för fall nr 1, men inte fall nr 2 och # 3. Typhuvud är kraftfulla komponenter, men de bör endast användas i fall där du vet att de är till hjälp för din medlem, varför vi bara vill visa vårt typhuvud när medlemmen skriver en hashtag.

Att återge typhuvudet eller inte att återge - Det är frågan

För att avgöra om typhuvudet ska visas måste vi hämta hashtaggen (vi kallar den aktiva hashtaggen) som medlemmen skapar / redigerar. För att göra det behöver vi:

1. En händelsehanterare för nybörjare som kommer att avfyras varje gång medlemmen trycker på en tangent (eller en tangentkombination som skift + a)

Händelseshanteraren är ganska enkel. Den kommer att ringa metoden getActiveHashtag och returnera den resulterande hashtaggen som den hittar:

// Händelsehanterare för när användaren trycker på // en tangent inuti postredigeraren onKeyPress = event => {const content = event.target.value; const key = event.key; const caretIndex = event.target.selectionStart; returnera getActiveHashtag (innehåll, nyckel, caretIndex)}

2. En funktion som heter getActiveHashtag som tar följande argument:

  • innehåll - Innehållet i det bildtexter som medlemmen redigerar. F.eks Hej #värld!
  • tangent - Nyckeln som medlemmen tryckte på för att utlösa den här händelsen med nedtonning. Vi hämtar detta från händelseobjektet. Om medlemmen trycker på en tangent, är event.key en. Det finns mer information tillgänglig som en del av händelseobjektet som kan avgöra om en tangentkombination har tryckts in (till exempel shift + A), men i vårt fall är event.key allt vi bryr oss om.
  • caretIndex - För att bestämma vilken del av posttexten medlemmen redigerar, måste vi veta var caret är. så att vi vet exakt vilken del av inläggeteksten medlemmen redigerar. Exempelvis om medlemsvakt är här: # hej worl | , då skulle caret-indexet vara 11. Vi får den här informationen från event.target.selectionStart.

Funktionen getActiveHashtag kommer antingen att returnera hashtaggen som medlemmen redigerar (t.ex. #worl), eller null om ingen hashtag aktivt redigeras av medlemmen.

Med dessa argument i åtanke och den önskade utgången (antingen en sträng som är hashtaggen eller null), låt oss bygga några pseudokodtestfall som vi kan använda för att avgöra om en medlem redigerar posttexten eller inte! Hos Tailwind gör vi Testdriven utveckling med hjälp av band för att minska fel och se till att vi har planerat vår implementering korrekt innan vi skriver den faktiska koden.

Fall 1 - Medlem skapar eller redigerar en hashtagg:

const test = () => {// Arranger const content = 'Hej #worl'; const key = 'l'; const caretIndex = 11; // Slut av raden // Act const r = getActiveHashtag (innehåll, nyckel, caretIndex); // Ange test.assert (r, `# worl`); };

Fall 2 - Medlemmens caret finns i en hashtag men medlemmen redigerar inte den:

const test = () => {// Arranger const content = 'Hej #world'; const key = 'ArrowLeft'; const caretIndex = 11; // Mellan `l` och` d` // Act const r = getActiveHashtag (innehåll, nyckel, caretIndex); // Ange test.assert (r, null); };

Fall 3 - Medlemmens caret är inte fokuserad på en hashtagg:

const test = () => {// Arranger const content = '# Hej worl'; const key = 'l'; const caretIndex = 11; // Slut av raden // Act const r = getActiveHashtag (innehåll, nyckel, caretIndex); // Ange test.assert (r, null); };

Nu när vi vet vilka resultat getActiveHashtag ska returnera, låt oss titta på implementeringen:

// Nycklar som aldrig använts för att // redigera en hashtag. const nonEditingKeys = ['ArrowLeft', 'ArrowRight', 'Control', 'Shift', // ... etc]; // Returnerar den aktivt redigerade hashtaggen, eller null // om ingen finns. const getActiveHashtag = (innehåll, nyckel, caretIndex) => {// Om användaren tryckte på en tangent som inte är ett tecken, redigerar de // inte aktivt en hashtag: if (nonEditingKeys.include (key)) {return null ; } // Ta reda på vilket ord eller hashtag som användaren redigerar // med hjälp av caret-positionen och innehållet: const activeWordOrHashtag = extractActiveWordOrHashtag (content, caretIndex); // om ordet som användaren redigerar är en hashtagg ska du returnera det. // annars, returnera null. returnera aktivWordOrHashtag [0] === '#'? activeWordOrHashtag: null; };

Du kommer att märka att vi har en stödjande funktion, extractActiveWordOrHashtag, som är ansvarig för att få ordet eller hashtaggen som medlemmen redigerar från posttexten. Så här ser det ut:

// Regex-mönster som matchar ett ord eller en hashtag. // Testa det här: [https://regex101.com/r/0Bl07o/2strong(https://regex101.com/r/0Bl07o/2) const hashtagOrWordRegex = /#*\w.*/g; // Hämtar ordet som användarens caret är placerad på. const extractActiveWordOrHashtag = (content, caretIndex) => {// Först backtrack tills vi hittar ett tecken som inte kan // vara en del av ett ord eller hashtag. låt index = caretIndex; låt karaktär = innehåll [index]; gör {let matches = char.match (hashtagOrWordRegex); // om det här tecknet inte är en del av en hashtag (t.ex. // det är ett mellanrum eller en period), returnera ordet eller // hashtaggen framför den. if (! matchar ||! matchningar.längd) {returnera innehåll. slice (index + 1, innehåll.längd) .match (hashtagOrWordRegex) [0]; } // Annars, gå till föregående teckenindex - = 1} medan (index> 0)}

Framför typhuvudet med React

Tack vare vår Keydown-hanterare och getActiveHashtag är logiken för huruvida skrivhuvudet ska visas eller inte på plats. I vår Keydown-hanterare returnerar vi resultatet av getActiveHashtag. Om detta resultat inte är noll, vet vi att vi måste göra skrivhuvudet; så vi kan skicka activeHashtag som en rekvisita till vår HashtagTypeahead-komponent och använda den i render-metoden så:

klass HashtagTypeahead utökar komponent {... render () {// Om användaren redigerar en hashtag, // gör typeaheaden för att ge // användarens förslag till hashtags att använda if (this.props.activeHashtag) {return ( )} // Annars returnera ingenting returnera null; }}

Jag abstraherade en del av React-implementeringsdetaljerna eftersom detta görs beror på det specifika typeahead-biblioteket som används; att välja ett typeahead-bibliotek och implementera det kan lätt vara sitt eget blogginlägg! På Tailwind använder vi React mycket och har några internt byggda komponenter som gör typhuvud för oss. Några utmärkta typhuvudkomponenter utanför hyllan inkluderar React Bootstrap Typeahead eller React Autosuggest. Dessa kan spara tid och energi på att bygga din egen typhuvud.

Detta är mitt första projekt sedan jag började på Tailwind och jag hade massor av kul att arbeta med det. Om du är intresserad av att lösa intressanta problem med frontend och backend, anställer vi! Om du har frågor, antingen om den här historien eller om Tailwind, kan du nå mig på [email protected]

Ursprungligen publicerad på https://martyjon.es den 19 april 2019.