点击一个可疑的div剧照外面的焦点?

cyt*_*nny 30 html javascript css

出于某种原因,我需要使用contenteditable div而不是普通的文本输入来输入文本.(对于一些javascript库)它工作正常,直到我发现当我设置contenteditable div使用时display: inline-block,即使我点击div之外,它也会给div提供焦点!

我需要它只有当用户点击div而不是它周围时才给焦点div.目前,我发现当用户点击其他地方然后单击与div相同的行的位置时,它会关注它.

一个显示问题的简单示例:

HTML:

<div class="outside">
    <div class="text-input" contenteditable="true">
        Input 1
    </div>
    <div class="text-input" contenteditable="true">
        Input 2
    </div>
    <div class="unrelated">This is some unrelated content<br>
      This is some more unrelated content
      This is just some space to shows that clicking here doesn't mess with the contenteditable div
      but clicking the side mess with it.
    </div>
</div>
Run Code Online (Sandbox Code Playgroud)

CSS:

div.outside {
  margin: 30px;
}
div.text-input {
  display:inline-block;
  background-color: black;
  color: white;
  width: 300px;
}
Run Code Online (Sandbox Code Playgroud)

用于显示问题的JSFiddle

是否有一种方法(CSS或javascript都可以接受)使浏览器只在点击它时给焦点而不是点击同一行?

PS我注意到有类似的问题(链接到其他相关的帖子),但情况有点不同,提供的解决方案对我不起作用.

Doc*_*cto 28

解释(如果您不在乎,请跳至下面的解决方法)

当您单击可编辑元素时,浏览器会将光标(也称为插入点)放置在单击元素内最近的文本节点中,与您单击的行位于同一行.文本节点可以直接位于被点击的元素内,也可以位于其子元素之一中.您可以通过运行下面的代码段并在大蓝框中单击来验证这一点.

.container {width: auto; padding: 20px; background: cornflowerblue;}
.container * {margin: 4px; padding: 4px;}
div {width: 50%; background: gold;}
span {background: orange;}
span > span {background: gold;}
span > span > span {background: yellow;}
Run Code Online (Sandbox Code Playgroud)
<div class="container" contenteditable>
  text in an editable element
  <div>
    text in a nested div
  </div>
  <span><span><span>text in a deeply nested span</span></span></span></div>
Notice that you can get an insertion point by clicking above the first line or below the last. This is because the "hitbox" of these lines extends to the top and bottom of the container, respectively. Some of the other answers don't account for this!
Run Code Online (Sandbox Code Playgroud)

蓝色框是<div>带有contenteditable属性的,而内部橙色/黄色框是嵌套的子元素.请注意,如果单击其中一个子元素附近(但不在其中),则光标会在其中结束,即使您在外部单击也是如此.这不是一个错误.由于您单击的元素(蓝色框)是可编辑的,并且子元素是其内容的一部分,因此将光标放在子元素中是有意义的,如果这是最接近的文本节点所在的位置.

问题是Webkit浏览器(Chrome,Safari,Opera)contenteditable在子项而不是父项上设置时表现出相同的行为.在这种情况下,浏览器甚至不应该费心寻找最近的文本节点,因为您实际点击的元素是不可编辑的.但Webkit确实如此,如果该文本节点恰好位于可编辑的子节点中,则会出现闪烁的光标.我认为这是一个错误; Webkit浏览器正在这样做:

on click:
  find nearest text node within clicked element;
  if text node is editable:
    add insertion point;
Run Code Online (Sandbox Code Playgroud)

......当他们应该这样做时:

on click:
  if clicked element is editable:
    find nearest text node within clicked element;
    add insertion point;
Run Code Online (Sandbox Code Playgroud)

块元素(例如div)似乎没有受到bug的影响,这让我觉得@GOTO 0的答案在暗示文本选择方面是正确的 - 至少在它看起来由控制插入的相同逻辑来管理点位置.在内联元素外部进行多次单击会突出显示其中的文本,但对于块元素则不然.当你在一个块外面点击时,你也没有得到一个插入点,这可能并非巧合.下面的第一个解决方法使用此异常.


解决方法1(嵌套div)

由于块不受bug的影响,我认为最好的解决方案是在内联块中嵌入div并使其可编辑.内联块已经在内部表现为块,因此div应该对其行为没有影响.

div.outside {
  margin: 30px;
}
div.text-input {
  display:inline-block;
  background-color: black;
  color: white;
  width: 300px;
}
Run Code Online (Sandbox Code Playgroud)
<div class="outside">
    <div class="text-input">
      <div contenteditable>
        Input 1
      </div>
    </div>
    <div class="text-input">
      <div contenteditable>
        Input 2
      </div>
    </div>
    <div class="unrelated">This is some unrelated content<br>
      This is some more unrelated content
      This is just some space to shows that clicking here doesn't mess with the contenteditable div
      but clicking the side mess with it.
    </div>
</div>
Run Code Online (Sandbox Code Playgroud)


解决方法2(不可见字符)

如果必须将该contenteditable属性放在内联块上,此解决方案将允许它.它通过围绕具有不可见字符(特别是零宽度空格)的内联块来工作,这使得它们不受外部点击的影响.(GOTO 0的答案使用相同的原理,但在我检查后仍然存在一些问题).

div.outside {
  margin: 30px;
}
div.text-input {
  display:inline-block;
  background-color: black;
  color: white;
  width: 300px;
  white-space: normal;
}
.input-container {white-space: nowrap;}
Run Code Online (Sandbox Code Playgroud)
<div class="outside">
  <span class="input-container">&#8203;<div class="text-input" contenteditable>
    Input 1
  </div>&#8203;</span>
  <span class="input-container">&#8203;<div class="text-input" contenteditable>
    Input 2
  </div>&#8203;</span>
  <div class="unrelated">This is some unrelated content<br>
      This is some more unrelated content
      This is just some space to shows that clicking here doesn't mess with the contenteditable div
      but clicking the side mess with it.
  </div>
</div>
Run Code Online (Sandbox Code Playgroud)


解决方法3(javascript)

如果你绝对无法改变你的标记,那么这个基于JavaScript的解决方案可以作为最后的手段(灵感来自这个答案).contentEditable单击内联块时设置为true,失去焦点时设置为false.

(function() {
  var inputs = document.querySelectorAll('.text-input');
  for(var i = inputs.length; i--;) {
    inputs[i].addEventListener('click', function(e) {
      e.target.contentEditable = true;
      e.target.focus();
    });
    inputs[i].addEventListener('blur', function(e) {
      e.target.contentEditable = false;
    });
  }
})();
Run Code Online (Sandbox Code Playgroud)
div.outside {
  margin: 30px;
}
div.text-input {
  display:inline-block;
  background-color: black;
  color: white;
  width: 300px;
}
Run Code Online (Sandbox Code Playgroud)
<div class="outside">
    <div class="text-input">
      Input 1
    </div>
    <div class="text-input">
      Input 2
    </div>
    <div class="unrelated">This is some unrelated content<br>
      This is some more unrelated content
      This is just some space to shows that clicking here doesn't mess with the contenteditable div
      but clicking the side mess with it.
    </div>
</div>
Run Code Online (Sandbox Code Playgroud)


GOT*_*O 0 9

我只能在Chrome和Safari中重现此行为,这表明这可能是与Webkit相关的问题.

在没有检查代码的情况下很难分辨出发生了什么,但我们至少可以怀疑问题在于触发浏览器中文本选择的一些错误机制.类比地,如果divs不是满足的,则在最后一个字符后单击同一行文本将触发从行尾开始的文本选择.

解决方法是将contenteditable divs 包装到容器元素中,并使容器样式化-webkit-user-select: none以使其无法选择.

正如Alex Char在评论中指出的那样,这不会阻止鼠标在容器外部点击以在其中的文本开头触发选择,因为第一个contenteditable div和(可选)祖先容器之间没有静态文本它.可能有更优雅的解决方案,但克服此问题的一种方法是在第一个有意义div的文本选择之前插入一个零宽度的不可见的非空文本范围.

  • 为什么非空?:因为在选择文本时会忽略空元素.
  • 零宽度为何?:因为我们不想看到它......
  • 为什么看不见?:因为我们不想要的内容被复制到与,比如说剪贴板Ctrl+A,Ctrl+C.

div.outside {
  margin: 30px;
}
div.text-input {
  display:inline-block;
  background-color: black;
  color: white;
  width: 300px;
}
div.text-input-container {
  -webkit-user-select: none;
}
.invisible {
  visibility: hidden;
}
Run Code Online (Sandbox Code Playgroud)
<div class="outside">
    <div class="text-input-container">
        <span class="invisible">&#8203;</span><div class="text-input" contenteditable="true">
            Input 1
        </div>
        <div class="text-input" contenteditable="true">
            Input 2
        </div>
    </div>
    <div class="unrelated">This is some unrelated content<br>
      This is some more unrelated content
      This is just some space to shows that clicking here doesn't mess with the contenteditable div
      but clicking the side mess with it.
    </div>
</div>
Run Code Online (Sandbox Code Playgroud)

即使在正常情况下,通常最好将相邻的内联块元素保存在单独的容器中,而不是在块元素(如不相关的元素div)旁边,以防止在兄弟元素的顺序发生变化时出现意外的布局效果.

  • 仍然无法在Chrome上运行.检查点击这些div的上方(在边距区域). (3认同)