Twitter 如何实现其 Tweet Box?
我正在尝试实现类似 Twitter 的推文框,具体来说:
I'm trying to implement something like Twitter's tweet box, specifically:
- 当总长度超过 140 个字符时,自动以红色背景突出显示文本.
- 自动以蓝色突出显示链接、提及和主题标签.
这些应该在用户键入时自动发生.
These should happen automatically aka when a user types.
根据我在 Twitter 上看到的语义标记,看起来他们正在使用 contentEditable
div.每当检测到提及/标签/链接时,或者当长度超过 140 个字符时,都会修改内部 DOM:
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>
到目前为止我做了什么
目前,我正在使用 contentEditable 字段.它触发一个解析文本的函数onChange/onInput
.通过正则表达式检查它是否具有用户名/链接/主题标签,并将它们替换为相应的标签(我现在只是使用一个简单的 <i>
标签来包含用户名和主题标签).我遇到的问题是,因为我正在更改/修改 contentEditable 的 DOM,所以插入符号光标位置会丢失.我查看了这个线程 坚持更改在 HTML 中选择后的范围对象,并且只有在 DOM 未更改时才能正常工作(在我的情况下,使用 <i>
标签包围标签/主题标签,链接使用 <a>
标签,并使用 <b>
标签溢出.
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.
小提琴:http://jsfiddle.net/4Lsqjkjb/
对于如何解决这个问题,是否有人有任何替代解决方案/方法?也许我不应该使用 contentEditable
?或者不直接修改 contentEditable
字段的 DOM 以保持插入符号位置的某种方式?任何帮助,将不胜感激!我尝试使用 window.getSelection()
并在 DOM 更改之前将其保存,但它似乎总是在应用 DOM 更改后重置.
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.
推荐答案
我所做的是一种 hack,但作为一种变通解决方案效果很好.
What I did is kind of a hack but works quite well as a workaround solution.
我有一个简单的 textarea(因为接下来会发生什么,所以不能 contenteditable,我将在下面解释)和一个绝对位于 textarea 后面的 div.使用 javascript,我将内容从 textarea 复制到 div,同时将其拆分为 140 个字符,并将所有额外字符放入 <em/>
标记中.
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.
嗯,它有点复杂,因为计算推文长度的推特算法是不同的(链接不算作它们的真实价值,因为 t.co url 缩短了).确切的方法可以在官方 twitter/twitter-txt 存储库中找到一个>.
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.
将一个简单的 textarea 包装成一个 div 以简化 css:
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 只是将 textarea 和 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;
}
现在有趣的是,这是使用 jQuery 实现的,但这不是最重要的.
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('');
}
在更改时添加一些事件处理程序,并准备好通用文档,您就完成了.请参阅下面的 codepen 链接.
Add some event handler on change, and te common document ready and you're done. See the codepen link below.
注意后面的div也可以用js创建,加载页面时:
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);
完整示例
在此处查看工作示例:http://codepen.io/hussard/pen/EZvaBZ
相关文章