6

How can i position my dropdown at cursor position inside a textarea? I have found this question was already asked here many times but i cant able figure out the correct solution ..

this is the JSBIN

please help me with your suggestions

Thanks in advance

4

2 回答 2

3

我知道这不是问题的确切答案(此解决方案不使用文本区域,而是使用 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它会继续工作(但它只会在你点击链接部分时触发)。

于 2013-06-26T10:53:11.527 回答
0

您可以获取鼠标的位置,然后将下拉列表移动到该位置。您只需要确保弹出内容的 z-index 高于您希望它遮挡的元素,并且它的位置设置为绝对。

这是我曾经写过的一个小测试样本。

<!DOCTYPE html>
<html>
<head>
<script>
function byId(e){return document.getElementById(e);}
function newEl(tag){return document.createElement(tag);}
function newTxt(txt){return document.createTextNode(txt);}
function toggleClass(element, newStr)
{
    index=element.className.indexOf(newStr);
    if ( index == -1)
        element.className += ' '+newStr;
    else
    {
        if (index != 0)
            newStr = ' '+newStr;
        element.className = element.className.replace(newStr, '');
    }
}
function forEachNode(nodeList, func)
{
    var i, n = nodeList.length;
    for (i=0; i<n; i++)
    {
        func(nodeList[i], i, nodeList);
    }
}

window.addEventListener('load', mInit, false);

function mInit()
{
}

function onShowBtn(e)
{
    var element = byId('popup');
    element.className = element.className.replace(' hidden', '');
    var str = '';//'border-radius: 32px; border: solid 5px;';
    e = e||event;
    str += "left: " + e.pageX + "px; top:"+e.pageY+"px;"
    element.setAttribute('style',str);
}
function onHideBtn()
{
    var element = byId('popup');
    if (element.className.indexOf(' hidden') == -1)
        element.className += ' hidden';
}

</script>
<style>
#controls
{
    display: inline-block;
    padding: 16px;
    border-radius: 6px;
    border: solid 1px #555;
    background: #AAA;
}
#popup
{
    border: solid 1px #777;
    border-radius: 4px;
    padding: 12px;
    background: #DDD;
    display: inline-block;
    z-index: 2;
    position: absolute;
}
#popup.hidden
{
    visibility: hidden;
}
</style>
</head>
<body>
    <div id='controls'>
        <input type='button' value='show' onclick='onShowBtn()'>
        <input type='button' value='hide' onclick='onHideBtn()'>
    </div>
    <br>
    <div id='popup'>
        <p>This is some assorted
            text</p>
            <hr>
        <ul>
            <li>item a</li>
            <li>item 2</li>
            <li>item iii</li>
        </ul>
    </div>
</body>
</html>
于 2013-06-25T11:27:17.063 回答