将焦点设置在可编辑div元素上

104

我有一个<div contenteditable=true>,我通过所见即所得编辑器定义了一些元素。例如<p><h1>等。我想直接将焦点放在这些元素之一上。

例如,在 <p id="p_test"> 上。但似乎 focus() 函数不适用于 <div> 元素、<p> 等元素...

是否有其他方法在我的情况下定义焦点?


6
由于div或p元素没有可用的操作,所以无法将其聚焦。你想聚焦什么? - Nick Craver
76
问题提到了内容可编辑,意味着您可以键入和编辑它等。在我看来,这是一个完全有效的问题。 - Blowsie
@Blowsie,我认为原问题并没有提到contenteditable功能。Nick Craver编辑了它并理清了思路。重点是焦点。 - Matej Svajger
var r=new Range(), sel=getSelection(); r.setStart(div.lastChild, div.lastChild.length||1);sel.removeAllRanges();sel.addRange(r) - caub
10个回答

95

旧的帖子,但是没有一个解决方案适用于我。不过最终我找到了解决方法:

var div = document.getElementById('contenteditablediv');
setTimeout(function() {
    div.focus();
}, 0);

8
不确定为什么,但这个方法完全奏效了!!将.focus()逻辑放入setTimeout中就解决了!谢谢! - sga4
1
不错!有些东西需要返回到调度程序才能被正确操作。浏览器很奇怪。谢谢! - B T
1
我只有在将代码放入其他元素的onClick处理程序中时才遇到过这种情况。 我猜测存在竞争条件:按钮被点击-> onClick处理程序聚焦某个元素-> 按钮获得焦点。 放置setTimeout将推迟代码,使其保持焦点。 - Vilius Paulauskas
8
我来自未来,这也是关于未来的工作。 - Bariq Dharmawan

44
在现代浏览器中,您可以使用:
var p = document.getElementById('contentEditableElementId'),
    s = window.getSelection(),
    r = document.createRange();
r.setStart(p, 0);
r.setEnd(p, 0);
s.removeAllRanges();
s.addRange(r);

但是如果你的元素是空的,我会遇到一些奇怪的问题。因此对于空元素,你可以这样做:

var p = document.getElementById('contentEditableElementId'),
    s = window.getSelection(),
    r = document.createRange();
p.innerHTML = '\u00a0';
r.selectNodeContents(p);
s.removeAllRanges();
s.addRange(r);
document.execCommand('delete', false, null);

删除 nbsp 后,光标仍停留在 p 元素内

附言:普通空格对我无效


5
在你的例子中,变量p是什么? - B T
2
@BT 我认为这是根P元素,尽管他应该明确说明,不留下任何疑问的空间。 - Savas Vedova
1
在众多提出的解决方案中,这是唯一一个对我有效的,允许我将焦点放在jquery对话框内的contenteditable div上。 - Steve Roberts
非常好的解决方案。谢谢 :) - Sunil Kumar
3
我在 r.setStart(p, 1)r.setEnd(p, 1) 中将零改为了一,以将光标放置在 contenteditable div 的末尾。我不知道为什么这样做有效,但它确实有效。谢谢! - Dave Land
哇,这真是个绝对的噩梦找到。 - user151496

32

我注意到jQuery的focus()方法不能用于宽度和高度为0的contenteditable DIV。然后我替换成使用原生的javascript focus()方法.get(0).focus(),这样就可以正常工作了。


3
哪个浏览器?在Safari中似乎无法工作,但Firefox和Chrome可以很好地处理它。 - Brian McCutchon
1
哪一部分?选择元素并使用focus(),例如:document.getElementById("#mydiv").focus()。 - Vlad

17

很多时候,如果无法调用.focus()方法来聚焦元素,那么这可能是因为该元素的tabIndex属性为-1,这意味着在按下Tab键进行导航时无法将焦点集中于此。

将tabIndex属性改为>=0即可让您聚焦到该元素上。如果需要动态执行此操作,则可以在事件侦听器中向元素添加tabIndex >=0。


6
这个回答应该被批准。“除非一个元素有一个tabindex属性,否则你不能在它上面调用.focus()方法。” - Ivan Hanák

7
你可以尝试这段代码,它可以自动聚焦到你最后插入的位置。
let p = document.getElementById('contenteditablediv')
let s = window.getSelection()
let r = document.createRange()
r.setStart(p, p.childElementCount)
r.setEnd(p, p.childElementCount)
s.removeAllRanges()
s.addRange(r)

谢谢,上面部分相同的答案对我没用,我需要添加 p.childElementCount 而不是你写的 1 - NoxFly


4

如果有人使用管理 contenteditable 元素的 MediumEditor 遇到此问题,以下方法适用于我:

editor.selectElement(editorDOMNode);
  1. editor is an instance of MediumEditor.
  2. editorDOMNode is a reference to the actual contenteditable DOM node.
  3. It is also possible to focus on a specific child node within the editor as follows:

    editor.selectElement(editorDOMNode.childNodes[0]);
    

4

还有一件需要检查的事情:如果您已经打开了浏览器的控制台窗口,请确保在加载页面时不要将焦点放在控制台上,否则页面上的元素将无法按预期获取焦点。

这也意味着您不能在控制台中运行document.querySelector('#your-elem').focus()(在2020年8月的Mac上尝试过Chrome、Safari和Firefox)。


救命稻草!在控制台中什么也没做,但是在实际代码中尝试这个:document.querySelector('.pell-content').focus(); 完美地为Pell RTE工作。太疯狂了,谢谢! - Vael Victus

0
编辑:我意识到这个问题实际上是在询问如何通过编程方式将光标放置在contenteditable div内的子元素上(而不是如何在用户交互中放置光标)。我尝试应用其他答案中的解决方案,但无法在代码小部件中使其正常工作。
请注意,click事件处理程序将需要点击以聚焦元素(而不触发插入符号),然后再次点击以触发插入符号。使用setTimeout.get(0)等方法将无法按预期工作。
相反,使用mousedown事件处理程序只需使用focus()调用即可按预期工作。无需其他解决方法。在Windows/Chrome(鼠标)和Android/Chrome(触摸)上进行了测试。
jQuery示例:

const ns = {
  focus: e => {
    let el = $(e.currentTarget);
    el.focus();
  }
}

$(document).ready(() => {
  $('body').on('mousedown', '[contenteditable]', ns.focus); //use mousedown instead of click

  let p = $('#auto_focus').get(0),
    s = window.getSelection(),
    r = document.createRange(),
    e = p.childElementCount > 0 ? p.lastChild : p;

  setTimeout(() => p.focus(), 0);

  //r.setStart(p, 0);
  //r.setEnd(p, 0);
  //r.setStart(e, 1);
  //r.setEnd(e, 1);
  r.setStart(p, p.childElementCount)
  r.setEnd(p, p.childElementCount)
  s.removeAllRanges();
  s.addRange(r);
});
[contenteditable] {
  border-bottom: 1px solid lightgray;
  /* prevents shifting on select */
  line-height: 1.2em;
}

[contenteditable]:focus {
  border-bottom: 1px solid black;
  /* prevents slight visual shifting on select */
  outline: 1px solid transparent;
}

[contenteditable=true]:empty:not(:focus):before {
  content: attr(placeholder) '...';
  font-style: italic;
  font-size: .9em;
  text-transform: lowercase;
  opacity: .3;
  font-weight: normal;
}

[contenteditable]>* {
  min-height: 1em;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div contenteditable=true placeholder="edit me"></div>
<div contenteditable=true placeholder="edit me, too"></div>
<div contenteditable=true>
  <p>paragraph 1</p>
  <p id="auto_focus">paragraph 2</p>
</div>


-12
将“click”事件处理程序绑定到contenteditable div中的所有元素,并在单击时更改类/样式(即向元素添加class“focused”);
您可以通过添加诸如彩色边框或内部box-shadow等样式来向用户标识具有焦点的元素。然后,当您想要在jQuery脚本中访问具有焦点的元素时,只需执行以下操作: var focused = $('.focused');

10
他询问的是实际将用户的光标移动到该元素,而不仅是样式化选定的元素。 - Brian McCutchon
1
这没有任何帮助。 - user4155172

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