Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does Twitter implement its Tweet Box?

I'm trying to implement something like Twitter's tweet box, specifically:

  • Automatically highlights text in a red background when the overall length exceeds 140 characters.
  • Automatically highlights links, mentions, and hashtags in blue.

These should happen automatically aka when a user types.

By the semantic markup I'm seeing on Twitter, it looks like they're using a contentEditable div. And the DOM inside is modified whenever a mention/hashtag/link is detected, or when the length is exceeded over 140 characters:

<div aria-labelledby="tweet-box-mini-home-profile-label" id="tweet-box-mini-home-profile" class="tweet-box rich-editor  notie" contenteditable="true" spellcheck="true" role="textbox" aria-multiline="true" dir="ltr" aria-autocomplete="list" aria-expanded="false" aria-owns="typeahead-dropdown-6">
    <div>hello world <a class="twitter-atreply pretty-link" href="/testMention" role="presentation"><s>@</s>testMention</a> text <a href="/search?q=%23helloWorld" class="twitter-hashtag pretty-link" role="presentation"><s>#</s>helloWorld</a> text <a href="http://www.google.com" class="twitter-timeline-link" role="presentation">http://www.google.com</a> text text text text text text text text text text text text text text <em>overflow red text here</em>
    </div>
</div>

What I have done so far

Currently, I'm using a contentEditable field. It triggers a function onChange/onInput that parses the text. Check if it has a username/link/hashtag via regexr and replace them with the respective tags (I'm just using a simple <i> tag to enclose username and hashtag for now). The problem I'm having is that because I'm changing/modifying the DOM of the contentEditable, the caret cursor position becomes lost. I looked at this thread Persisting the changes of range objects after selection in HTML and it works fine only if the DOM isn't changed (which it is in my case, surrounding tags/hashtags with <i> tags, links with <a> tags, and overflow with <b> tags.

Fiddle: http://jsfiddle.net/4Lsqjkjb/

Does anyone have any alternative solutions/approaches on how to tackle this problem? Maybe I shouldn't use contentEditable? Or some way that doesn't directly modify the DOM of the contentEditable field so the caret position is maintained? Any help would be appreciated! I tried playing around with window.getSelection() and saving it before DOM change, but it always seem to reset after DOM changes are applied.

like image 731
trekforever Avatar asked Mar 02 '15 22:03

trekforever


People also ask

What is a Tweet box?

Tweet-Box is a CommandBox Command that lets you post status updates and send direct messages through Twitter via the CLI.

How do Twitter tweets work?

Compose your message (up to 280 characters) and tap Tweet. Enter your message (up to 280 characters), and then tap Tweet. A notification will appear in the status bar on your device and will go away once the Tweet successfully sends.

What is the structure of Twitter?

The Broadcast Network Twitter Community Structure has a hub and spoke structure. At the hub is a media outlet or influential social media figure. The spokes are people who repeat the hub's message. The audience is generally only connected to the information source, not each other.


1 Answers

What I did is kind of a hack but works quite well as a workaround solution.

I've got a simple textarea (not contenteditable because of what's next, I'll explain below) and a div which is absolute positioned behind the textarea. Using javascript, I copy the content from the textarea to the div while splitting it at 140 chars and putting all extra characters inside an <em /> tag.

Well, it's a little bit more complicated because twitter algorithm to compute a tweet length is different (links doesn't count as their real value, because of t.co url shortening). The exact method can be found as part of the official twitter/twitter-txt repository.

Step-by-step code.

Wrap a simple textarea into a div to simplify the css:

<div class="tweet-composer">
  <textarea class="editor-textarea js-keeper-editor">This is some text that will be highlight when longer than 20 characters. Like twitter. Type something...</textarea>
  <div class="js-keeper-placeholder-back"></div>
</div>

CSS is just making the textarea and the div right above each other and highlight the text.

.tweet-composer {
  position: relative;
  z-index: 1;
}

.js-keeper-editor,
.js-keeper-placeholder-back {
  background: transparent;
  border: 1px solid #eee;
  font-family: Helvetica, Arial, sans-serif;
  font-size: 14px; /* Same font for both. */
  margin: auto;
  min-height: 200px;
  outline: none;
  padding: 10px;
  width: 100%;
}

.js-keeper-placeholder-back {
  bottom: 0;
  color: transparent;
  left: 0;
  position: absolute;
  top: 0;
  white-space: pre-wrap;
  width: 100%;
  word-wrap: break-word;
  z-index: -1;
}

.js-keeper-placeholder-back em {
  background: #fcc !important;
}

Now the fun part, this is implemented using jQuery but that's not the important thing.

if (0 > remainingLength) {
  // Split value if greater than 
  var allowedValuePart = currentValue.slice(0, realLength),
      refusedValuePart = currentValue.slice(realLength)
  ;

  // Fill the hidden div.
  $placeholderBacker.html(allowedValuePart + '<em>' + refusedValuePart + '</em>');
} else {
  $placeholderBacker.html('');
}

Add some event handler on change, and te common document ready and you're done. See the codepen link below.

Note that the div placed behind can be created using js too, when loading the page:

// Create a pseudo-element that will be hidden behind the placeholder.
var $placeholderBacker = $('<div class="js-keeper-placeholder-back"></div>');
$placeholderBacker.insertAfter($textarea);

Full example

See working example over here: http://codepen.io/hussard/pen/EZvaBZ

like image 187
Hussard Avatar answered Sep 19 '22 20:09

Hussard