我知道这不是问题的确切答案(此解决方案不使用文本区域,而是使用 contentEditable div),但我认为没有任何方法可以使用事件、属性来获取 xy 坐标或 textarea 上的函数或 Selection 对象上的属性或函数。
我在 JSBin 上整理了一个示例。请注意,我没有费心测试其他浏览器的兼容性,并且它不会将插入符号返回到您离开的位置。我无法弄清楚它的代码。我相信window.getSelection()
在 IE 中不起作用,在 IE8 中它会完全不同。您可能还想确保菜单不会直接从屏幕边缘显示。
的HTML
<div id="target" contentEditable="true">Type @ to see the dropdown.... </div>
<div class="dropdown">
<ul id="dropdown" class="dropdown-menu hide" role="menu" aria-labelledby="dropdownMenu">
<li><a>One</a> </li>
<li><a>Two</a></li>
<li><a>Three</a></li>
<li><a>Four</a> </li>
</ul>
</div>
CSS
#target {
height: 100px;
border: 1px solid black;
margin-top: 50px;
}
#dummy {
display: inline-block;
}
.dropdown {
position: absolute;
top: 0;
left: 0;
}
Javascript 和 JQuery
$("#target").keydown( function(e) {
if(e.which === 50 && e.shiftKey === true ) {
//Prevent this event from actually typing the @
e.preventDefault();
//console.log( window.getSelection() );
var sel = window.getSelection();
var offset = sel.baseOffset;
var node = sel.focusNode.parentNode;
//Get the text before and after the caret
var firsttext = node.innerHTML.substr(0,sel.baseOffset);
var nexttext = (sel.baseOffset != sel.focusNode.length ) ? node.innerHTML.substr( sel.baseOffset, sel.focusNode.length) : "";
//Add in @ + dummy, because @ is not in there yet on keydown
node.innerHTML = firsttext + '@<div id="dummy"></div>' + nexttext;
//Transfer all relevant data to the dropdown menu
$('.dropdown').css('left', $('#dummy')[0].offsetLeft).css('top', $('#dummy')[0].offsetTop).prop('x-custom-offset', offset + 1);
//Delete the dummy to keep it clean
//This will split the contents into two text nodes, which we don't want
//$('#dummy').remove();
node.innerHTML = firsttext + '@' + nexttext;
//Put the caret back in place where we left off
//...I can't seem to figure out how to correctly set the range correctly...
$('#dropdown').removeClass('hide').addClass('show');
} else {
$('#dropdown').removeClass('show').addClass('hide');
$('.dropdown').removeProp('x-custom-offset');
}
});
$('#dropdown').on( 'click', 'li a', function( e ) {
e.preventDefault();
$('#target').html( function( i, oldtext ) {
var firsttext = oldtext.substr( 0, $('.dropdown').prop('x-custom-offset') );
var nexttext = oldtext.substr( $('.dropdown').prop('x-custom-offset'), oldtext.length );
console.log( e );
var inserttext = e.target.innerText;
//Cleanup
$('#dropdown').removeClass('show').addClass('hide');
return firsttext + inserttext + nexttext;
} );
} );
说明
此示例的工作基于您可以在 contentEditable 中插入一个元素并检索它在屏幕顶部和左侧的偏移量。当按下 shift + 键 50 时,事件处理程序将阻止 @ 被写入,而是插入 @ + 虚拟对象本身。然后我们从该对象检索偏移量并将下拉菜单移动到该偏移量。此外,我们将字符偏移保存为x-custom-offset
菜单的自定义属性,以便我们可以在该特定位置插入一个值。然后我们需要删除虚拟 div,但如果我们要删除虚拟$('#dummy').remove()
虚拟之前的文本节点和虚拟后面的文本节点不会合并。如果我们要在某处放置另一个 @ 和/或将其放置在错误的位置,这将删除最后一个文本节点。因此,我们再次简单地替换可编辑 div 的内容。最后,插入符号必须放回原来的位置。我不知道如何正确地做到这一点。
第二个处理程序是将文本插入文本框。代码应该是不言自明的。我们之前设置的x-custom-offset
属性在这里用于将文本插入到文本框中的正确位置。$('#dropdown').on( 'click', 'li a', function( e ) { ... } );
将点击事件附加到ul
而不是li
's,这样如果你动态创建li
's它会继续工作(但它只会在你点击链接部分时触发)。