contenteditable中的占位符 - focus事件问题

68

我之前一直在试图提出问题,但并没有成功地解释或提供一个能够展示错误发生的工作示例。所以,这里再试一次:

我正在尝试在可编辑DIV上复制占位符效果。核心概念很简单:

<div contenteditable><em>Edit me</em></div>

<script>
$('div').focus(function() {
    $(this).empty();
});
</script>

这种方法有时可以奏效。但是如果占位符包含HTML,或者进行了其他处理,则可编辑DIV的文本插入符将被移除,用户必须重新单击可编辑DIV才能开始输入(即使它仍然处于焦点状态):

例如:http://jsfiddle.net/hHLXr/6/

我无法在处理程序中使用焦点触发器,因为这会创建一个事件循环。因此,我需要一种重新设置可编辑DIV中插入符光标的方法,或以其他方式重新聚焦。

11个回答

168

这里是一个仅使用CSS的解决方案,补充了其他答案中的一些内容:

<div contentEditable=true data-ph="My Placeholder String"></div>
<style>
    [contentEditable=true]:empty:not(:focus)::before{
        content:attr(data-ph)
    }
</style>

编辑:这是我在Codepen上的代码片段 -> http://codepen.io/mrmoje/pen/lkLez

编辑2:请注意,由于在对所有行执行“选择全部剪切”或“选择全部删除”后,<br>元素仍然存在于div中,因此此方法无法100%适用于多行应用程序。致谢:- @vsync
退格键似乎工作得很好(至少在webkit / blink上)


1
这个东西看起来非常酷,但是...我无法让它工作。你能发布一个可用的jsfiddle吗? - ccleve
7
这应该是被采纳的答案。它比@amwinter的答案稍微复杂一些,但它在html中保留了占位符,而不是在css中更改。 - ccleve
1
真的!复杂度权衡的另一个优点是,如果您有多个contentEditable元素,您只需要为每个指定一个data-ph占位符,同样的CSS代码就可以处理所有这些元素。 - mrmoje
1
这不是答案,它会失败并且不可靠。尝试写入两行文本,然后选择全部并删除。在 contentEditable 中仍将存在一些垃圾 HTML,这将阻止触发 :empty。失败了。 - vsync
1
你说得对,@vsync!这是一个非常好的发现。谢谢你分享。 - mrmoje
显示剩余10条评论

29

我刚刚发布了一个插件

它使用CSS3和JavaScript的组合,在不添加到div内容的情况下显示占位符:

HTML:


<div contenteditable='true' data-placeholder='Enter some text'></div>

CSS:

div[data-placeholder]:not(:focus):not([data-div-placeholder-content]):before {
    content: attr(data-placeholder);
    float: left;
    margin-left: 5px;
    color: gray;
}

JS:

(function ($) {
    $('div[data-placeholder]').on('keydown keypress input', function() {
        if (this.textContent) {
            this.dataset.divPlaceholderContent = 'true';
        }
        else {
            delete(this.dataset.divPlaceholderContent);
        }
    });
})(jQuery);

就这样了。


1
自从我写下这个答案以来,我已更新了插件以增强IE兼容性和非div元素。请查看关联的GitHub仓库获取最新版本。 - Craig Stuntz
如果我从后端设置 div 文本,它能正常工作吗?在我的情况下,占位符和我的文本是连接在一起的。 - demo

25

您可能需要手动更新选择。在IE中,焦点事件太晚了,所以我建议使用activate事件。以下是一些代码,可以在所有主流浏览器中执行此操作,包括IE<= 8(无法使用仅CSS的替代方案):

Live demo: http://jsfiddle.net/hHLXr/12/

代码:

$('div').on('activate', function() {
    $(this).empty();
    var range, sel;
    if ( (sel = document.selection) && document.body.createTextRange) {
        range = document.body.createTextRange();
        range.moveToElementText(this);
        range.select();
    }
});

$('div').focus(function() {
    if (this.hasChildNodes() && document.createRange && window.getSelection) {
        $(this).empty();
        var range = document.createRange();
        range.selectNodeContents(this);
        var sel = window.getSelection();
        sel.removeAllRanges();
        sel.addRange(range);
    }
});

再次为RangeMaster点赞。我原以为保持简单就能避免跨浏览器问题,但这种想法太天真了。 - Andy E
太好了!我希望您在,RangeMaster先生! - David Hellsing
1
@AndyE:谢谢。可能有更简单的方法。实际上应该有的,但我没能快速找到。 - Tim Down
这两天我一直在为这个问题苦恼,尝试了各种方法。我甚至在我的头后面添加了一个绝对定位的占位符,以便这个解决方案对我来说足够简单!不过我同意,应该有更简单的解决方案... - David Hellsing
太棒了!你为我省去了很多麻烦 :) - johnjohn

24

只需使用CSS伪类。

span.spanclass:empty:before {content:"placeholder";}

1
这很棒。然后你可以使用 span.spanclass:active:before{content:""} 在选中时将其移除。 - BobtheMagicMoose
@BobtheMagicMoose,谢谢你!看起来是解决一个非常恼人的问题的绝妙解决方案。 - Steve

14
我发现最好的方法是像平常一样使用“placeholder”属性,然后添加几行CSS代码。
HTML:
<div contenteditable placeholder="I am a placeholder"></div>

CSS(层叠样式表)
[contenteditable][placeholder]:empty:before {
    content: attr(placeholder);
    color: #bababa;
}

注意:CSS的:empty选择器只有在标签的开头和结尾之间确实没有任何内容时才起作用。这包括换行、制表符、空格等。

Codepen


(注:此处为链接文字,应根据上下文进行翻译)

这个解决方案在FF中存在问题。如果您在靠近右侧边框的位置点击两次,光标会停留在那里。 - mayankcpdixit

11

你所需的只是这个小解决方案。

[contenteditable=true]:empty:before{
  content: attr(placeholder);
  display: block; /* For Firefox */
}

演示: http://codepen.io/flesler/pen/AEIFc


可以正常工作。在Firefox、Chrome和Safari中进行了测试。 - Joel Caton
眼睛,而不是边缘(光标出现在末尾) - cgat
截至2020年8月,这在Edge中可行(无论是macOS版本还是其他版本)。 - NetOperator Wibby
2
在Chrome中使用此方法时遇到了一个问题,如果您在占位符上单击右键,则必须点击两次才能放置插入符号。通过添加pointer-events: none;(在您的codepen css中找到)来解决了这个问题。在这里提及,为那些正在寻找特定Chrome问题解决方案的人。 - Guganeshan.T

4
这是我的方法:

它使用了jQuery和CSS3的组合。 与html5的placeholder属性完全相同!

  • 当您输入第一个字母时,立即隐藏自己
  • 当您删除输入的内容时,再次显示自己

HTML:

<div class="placeholder" contenteditable="true"></div>

CSS3:

.placeholder:after {
    content: "Your placeholder"; /* this is where you assign the place holder */
    position: absolute;
    top: 10px;
    color: #a9a9a9;
}

jQuery:

$('.placeholder').on('input', function(){
    if ($(this).text().length > 0) {
        $(this).removeClass('placeholder');
    } else {
        $(this).addClass('placeholder');
    }
});

DEMO: http://jsfiddle.net/Tomer123/D78X7/


“input” 事件在 contenteditable 元素中是相当新的。例如,在 IE 中根本不支持它。 - Tim Down

1
这是我使用的修复方法。
<div contenteditable><em>Edit me</em></div>
<script>
$('div').focus(function() {
    var target = $(this);
    window.setTimeout(function() { target.empty(); }, 10);
});
</script>

我为此开发了一个jQuery插件。请查看https://github.com/phitha/editableDiv

0
var curText = 'Edit me';
$('div').focusin(function() {
    if ($(this).text().toLowerCase() == curText.toLowerCase() || !$(this).text().length) {
        $(this).empty();
    }
}).focusout(function() {
    if ($(this).text().toLowerCase() == curText.toLowerCase() || !$(this).text().length) {
        $(this).html('<em>' + curText + '</em>');
    }
});

谢谢,但问题不在于我不知道如何保存占位符文本,而是当清除占位符文本时插入符号消失的事实。 - David Hellsing

0

这不是你问题的确切解决方案...

在summernote选项中设置

airMode:true

占位符以这种方式工作。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接