【问题标题】:How to highlight friends name in Facebook status update box (textarea)?如何在 Facebook 状态更新框 (textarea) 中突出显示好友姓名?
【发布时间】:2011-11-21 19:21:50
【问题描述】:

在 Facebook 状态更新框中,当我输入 @ 并开始输入并从 fb 建议的朋友列表中选择一个名字(比如 Steven Gerrard)时,我朋友的名字在 textarea 中像这样突出显示

我用 Firebug 查了一下,只有

  • 一个 div.highlighter,其中包含某种格式化文本(Steven Gerrard 在 b 标签内)
  • div.uiTypeahead 中的文本区域。我找不到任何有趣的东西
  • 还有一个隐藏的输入,其中包含将要发布的实际文本:@[100001915747xxx:Steven Gerrard] 很棒

这背后的秘诀是什么?像 ckeditor 这样的普通富文本编辑器通常有一个 iframe 来显示文本和一个实际的 textarea 来保留原始内容。但在这种情况下,我什么也没看到。有人请点亮一些灯吗?

我想做这样的事情,但不知道从哪里开始。另外,如果我想在我朋友的名字旁边显示一个小拇指,有可能吗?

【问题讨论】:

    标签: facebook textarea rich-text-editor


    【解决方案1】:

    它的工作原理如下:

    • 您将 textarea(前面)和 div(后面)叠加在一起,它们将具有相同的大小和相同的字体大小。
    • textarea必须有透明背景,这样我们才能看到它的文字,也能看到它后面的div。
    • 它后面的div会有一个白色的文字和白色的背景,所以它包含的文字是透明的。
    • 您在 textarea 的 keyup 上设置了一个挂钩,并将其中包含的文本处理为 HTML:将换行符替换为
      ,将双空格替换为  &nbsp ;,并将所有要突出显示的单词替换为由 包围的版本。李>
    • 由于您可以看到 textarea 后面的高亮 div,并且高亮 div 包含的文本与 textarea 中的文本完全对齐,并且 是可见的,因此您会产生一种错觉,即文本区域被突出显示。

    我已经写了一个基于 jquery 的简单示例,因此您可以自己尝试,无需分析太多代码。


    这是一个示例代码,您可以复制粘贴保存并尝试:

    此示例代码将突出显示一组已定义的单词,此处为:“hello”和“world”。

    我会让你随心所欲地调整它。

    <html>
        <head>
            <title></title>
            <!-- Load jQuery -->
            <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4/jquery.min.js"></script>
            <!-- The javascript xontaining the plugin and the code to init the plugin -->
            <script type="text/javascript">
                $(function() {
                    // let's init the plugin, that we called "highlight".
                    // We will highlight the words "hello" and "world", 
                    // and set the input area to a widht and height of 500 and 250 respectively.
                    $("#container").highlight({
                        words:  ["hello","world"],
                        width:  500,
                        height: 250
                    });
                });
    
                // the plugin that would do the trick
                (function($){
                    $.fn.extend({
                        highlight: function() {
                            // the main class
                            var pluginClass = function() {};
                            // init the class
                            // Bootloader
                            pluginClass.prototype.__init = function (element) {
                                try {
                                    this.element = element;
                                } catch (err) {
                                    this.error(err);
                                }
                            };
                            // centralized error handler
                            pluginClass.prototype.error = function (e) {
                                // manage error and exceptions here
                                //console.info("error!",e);
                            };
                            // Centralized routing function
                            pluginClass.prototype.execute = function (fn, options) {
                                try {
                                    options = $.extend({},options);
                                    if (typeof(this[fn]) == "function") {
                                        var output = this[fn].apply(this, [options]);
                                    } else {
                                        this.error("undefined_function");
                                    }
                                } catch (err) {
                                    this.error(err);
                                }
                            };
                            // **********************
                            // Plugin Class starts here
                            // **********************
                            // init the component
                            pluginClass.prototype.init = function (options) {
                                try {
                                    // the element's reference ( $("#container") ) is stored into "this.element"
                                    var scope                   = this;
                                    this.options                = options;
    
                                    // just find the different elements we'll need
                                    this.highlighterContainer   = this.element.find('#highlighterContainer');
                                    this.inputContainer         = this.element.find('#inputContainer');
                                    this.textarea               = this.inputContainer.find('textarea');
                                    this.highlighter            = this.highlighterContainer.find('#highlighter');
    
                                    // apply the css
                                    this.element.css('position','relative');
    
                                    // place both the highlight container and the textarea container
                                    // on the same coordonate to superpose them.
                                    this.highlighterContainer.css({
                                        'position':         'absolute',
                                        'left':             '0',
                                        'top':              '0',
                                        'border':           '1px dashed #ff0000',
                                        'width':            this.options.width,
                                        'height':           this.options.height,
                                        'cursor':           'text'
                                    });
                                    this.inputContainer.css({
                                        'position':         'absolute',
                                        'left':             '0',
                                        'top':              '0',
                                        'border':           '1px solid #000000'
                                    });
                                    // now let's make sure the highlit div and the textarea will superpose,
                                    // by applying the same font size and stuffs.
                                    // the highlighter must have a white text so it will be invisible
                                    this.highlighter.css({
    
                                        'padding':          '7px',
                                        'color':            '#eeeeee',
                                        'background-color': '#ffffff',
                                        'margin':           '0px',
                                        'font-size':        '11px',
                                        'font-family':      '"lucida grande",tahoma,verdana,arial,sans-serif'
                                    });
                                    // the textarea must have a transparent background so we can see the highlight div behind it
                                    this.textarea.css({
                                        'background-color': 'transparent',
                                        'padding':          '5px',
                                        'margin':           '0px',
                                        'font-size':        '11px',
                                        'width':            this.options.width,
                                        'height':           this.options.height,
                                        'font-family':      '"lucida grande",tahoma,verdana,arial,sans-serif'
                                    });
    
                                    // apply the hooks
                                    this.highlighterContainer.bind('click', function() {
                                        scope.textarea.focus();
                                    });
                                    this.textarea.bind('keyup', function() {
                                        // when we type in the textarea, 
                                        // we want the text to be processed and re-injected into the div behind it.
                                        scope.applyText($(this).val());
                                    });
                                } catch (err) {
                                    this.error(err);
                                }
                                return true;
                            };
                            pluginClass.prototype.applyText = function (text) {
                                try {
                                    var scope                   = this;
    
                                    // parse the text:
                                    // replace all the line braks by <br/>, and all the double spaces by the html version &nbsp;
                                    text = this.replaceAll(text,'\n','<br/>');
                                    text = this.replaceAll(text,'  ','&nbsp;&nbsp;');
    
                                    // replace the words by a highlighted version of the words
                                    for (var i=0;i<this.options.words.length;i++) {
                                        text = this.replaceAll(text,this.options.words[i],'<span style="background-color: #D8DFEA;">'+this.options.words[i]+'</span>');
                                    }
    
                                    // re-inject the processed text into the div
                                    this.highlighter.html(text);
    
                                } catch (err) {
                                    this.error(err);
                                }
                                return true;
                            };
                            // "replace all" function
                            pluginClass.prototype.replaceAll = function(txt, replace, with_this) {
                                return txt.replace(new RegExp(replace, 'g'),with_this);
                            }
    
                            // don't worry about this part, it's just the required code for the plugin to hadle the methods and stuffs. Not relevant here.
                            //**********************
                            // process
                            var fn;
                            var options;
                            if (arguments.length == 0) {
                                fn = "init";
                                options = {};
                            } else if (arguments.length == 1 && typeof(arguments[0]) == 'object') {
                                fn = "init";
                                options = $.extend({},arguments[0]);
                            } else {
                                fn = arguments[0];
                                options = $.extend({},arguments[1]);
                            }
    
                            $.each(this, function(idx, item) {
                                // if the component is not yet existing, create it.
                                if ($(item).data('highlightPlugin') == null) {
                                    $(item).data('highlightPlugin', new pluginClass());
                                    $(item).data('highlightPlugin').__init($(item));
                                }
                                $(item).data('highlightPlugin').execute(fn, options);
                            });
                            return this;
                        }
                    });
    
                })(jQuery);
    
    
            </script>
        </head>
        <body>
    
            <div id="container">
                <div id="highlighterContainer">
                    <div id="highlighter">
    
                    </div>
                </div>
                <div id="inputContainer">
                    <textarea cols="30" rows="10">
    
                    </textarea>
                </div>
            </div>
    
        </body>
    </html>
    

    如果您有任何问题或需要有关此代码的帮助,请告诉我。

    【讨论】:

    • 嗨朱利安,谢谢我给了你赏金。我现在明白了。现在的问题是,当您调整 textarea 的大小时,div 不会随之调整大小,因此 2 层之间会出现不匹配,如果您可以在代码中解决这个问题,那就太好了。我需要一些时间来研究你的代码
    • 谢谢 :)我不知道你用什么方法来调整 textarea 的大小,但原则保持不变:每当你调整 textarea 的大小时,你必须将它后面的 div 调整为相同的大小精确大小,因此文本将保持对齐。
    • @JulienL,div 上的文本不像 textarea 中的文本那样剪切。有什么想法吗?
    • 仅供参考,@Julien L 简洁的代码已被用作支持调整大小的 jQuery 插件的基础,包括 &lt;textarea&gt;&lt;input&gt; 元素等等。如果您喜欢插件,请查看:github.com/mistic100/jQuery-highlightTextarea
    【解决方案2】:

    查看Facebook的做法后,我看到屏幕上显示的文字是:

    <span class="highlighterContent"><b>Ws Dev</b> is good</span>
    

    该跨度被放在一个表格中(有很多 div 容器),这是相应的样式。

    所以我认为是这个过程:

    1. 当您在框中输入内容时,Facebook 确实有一个文本区域来捕获您输入的内容,但使用 javascript 在表格中显示输入的 HTML 内容。

    2. 当您提交时,隐藏输入中的格式化内容(您已经在问题中发现)被提交。就像“@[100001915747xxx:Steven Gerrard] 太棒了”。

    3. 当格​​式化的消息提交时,它被保存到数据库中。每次加载页面时,都会从保存的消息中编写 HTML 并返回。

    要获得类似的效果,您可以使用任何jQuery autocomplete plugin

    【讨论】:

    • 感谢 Hoang Long,但是如何将 span 覆盖在 textarea 的顶部,同时仍然让 textarea 看起来像是正在编辑?
    • @clu3:说实话,在阅读您的问题之前,我不会考虑它。我做了一些研究,并找到了解决方案:将 textarea 放在透明的顶部。你可以参考这个问题:stackoverflow.com/questions/3282505/…
    • 非常感谢。看起来棒极了。我会试一试,看看结果如何
    猜你喜欢
    • 2010-12-19
    • 1970-01-01
    • 2014-09-13
    • 1970-01-01
    • 1970-01-01
    • 2016-04-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多