我如何将一个下拉菜单放置在文本区域内的光标位置? [英] How can i position a dropdown at cursor position inside a textarea?

查看:188
本文介绍了我如何将一个下拉菜单放置在文本区域内的光标位置?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如何将我的下拉菜单放置在textarea内的光标位置?我发现这个问题已经提出了很多次,但我无法找出正确的解决方案..



这是 JSBIN



请帮助我提出您的建议



提前感谢

解决方案

我知道这不是一个确切的答案问题(此解决方案不使用textarea ,但是一个contentEditable div),但我不认为有任何方法使用事件,textarea上的一个属性或函数或Selection对象上的一个属性或函数获得xy坐标。



我创建了一个示例对JSBin 。请注意,我没有在其他浏览器的兼容性测试,并且它不会返回插入符到你离开的地方。我不知道的代码。我相信 window.getSelection()将无法在IE中工作,在IE8-它将完全不同。






strong> HTML

 < div id =targetcontentEditable =true>请参阅下拉...。< / div> 
< div class =dropdown>
< ul id =dropdownclass =dropdown-menu hiderole =menuaria-labelledby =dropdownMenu>
< li>< a>一个< / a> < / li>
< li>< a>两个< / a>< / li>
< li>< a>三< / a>< / li>
< li>< a>四< / 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){
//防止此事件实际键入@
e.preventDefault();

//控制台。 log(window.getSelection());
var sel = window.getSelection();

var offset = sel.baseOffset;
var node = sel.focusNode.parentNode;

//获取光标前后的文本
var firsttext = node.innerHTML.substr(0,sel.baseOffset);
var nexttext =(sel.baseOffset! = sel.focusNode.length)?node.innerHTML.substr(sel.baseOffset,sel.focusNode.length):;

//添加@ + dummy,因为@不在但是在键盘上
node.innerHTML = firsttext +'@< div id =dummy>< / div>'+ nexttext;

//将所有相关数据传输到下拉菜单

$('。dropdown')。css('left',$('#dummy')[0] .offsetLeft).css('top',$(' )[0] .offsetTop).prop('x-custom-offset',offset + 1);

//删除哑元以保持其清洁
//这将把内容分成两个文本节点,我们不想要
// $('#dummy ')。去掉();
node.innerHTML = firsttext +'@'+ nexttext;

//将插入符号放回我们离开的地方
//...我似乎无法找出如何正确设置范围...

$('#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')。
var nexttext = oldtext.substr($('。dropdown')。prop('x-custom-offset'),oldtext.length);

console.log(e);

var inserttext = e.target.innerText;

//清除
$('#dropdown')。removeClass('show')。addClass hide');

return firsttext + inserttext + nexttext;
});
});






说明 / p>

此示例的工作原理是您可以在contentEditable中插入元素,并检索其偏移到屏幕的顶部和左侧。当按下shift +键50时,事件处理程序将阻止写入@,而是插入@ +虚拟对象本身。然后我们从这个对象检索偏移量,并将下拉菜单移动到该偏移量。此外,我们将character-offset保存为菜单的自定义属性 x-custom-offset ,以便我们可以在该特定位置插入值。然后,我们需要删除虚拟div,但是如果我们删除了 $('#dummy')。虚拟和文本之前的文本节点的remove()节点后面的虚拟将不会合并。这将删除最后一个textnode,如果我们把一个其他@某处和/或将它放在错误的位置。因此,我们只是再次替换可编辑div的内容。最后,插入符必须设置回其原始位置。我不能弄清楚如何正确地这样做。



第二个处理程序是在文本框中插入文本。代码应该是自解释的。在此使用我们之前设置的 x-custom-offset 属性将文本插入到文本框中的正确位置。 $('#dropdown')。on('click','li a',function(e){...}); 会将click事件 ul 而不是 li ,所以如果你动态创建 li (但只有在点击链接部分时才会触发)。


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

解决方案

I know it isn't an exact answer on the question (this solution doesn't use a textarea, but a contentEditable div), but I don't think there is any way of getting x-y-coordinates using either the event, an attribute or function on the textarea or an attribute or function on the Selection object.

I have meshed up an example on JSBin. Please note that I haven't bothered testing for compatibility in other browsers and that it won't return the caret to where you left off. I can't figure out the code for that. I believe window.getSelection() will not work in IE, and in IE8- it would be completely different. You probably want to make sure too, that the menu will not be displayed right from the edge of the screen.


The 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>

The CSS

#target {
  height: 100px;
  border: 1px solid black;
  margin-top: 50px;
}

#dummy {
  display: inline-block;
}

.dropdown {
  position: absolute;
  top: 0;
  left: 0;
}

The 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;
  } );
} );


The explanation

This example works based on that you can insert an element in a contentEditable and retrieve it's offset to the top and the left of the screen. When shift + key 50 is pressed, the event handler will prevent the @ from being written and instead inserts the @ + dummy object itself. Then we retrieve the offset from this object and move the dropdown menu to that offset. Furthermore, we save the character-offset as a custom property x-custom-offset of the menu, so that we can insert a value at that specific location. We then need to remove the dummy div, but if we would remove the dummy with $('#dummy').remove() the text node before the dummy and the text node behind the dummy will not merge. This will delete the last textnode if we were to put an other @ somewhere and/or place it in the wrong location. Therefore, we simply replace the contents of the editable div again. Last, the caret must be set back to it's original position. I cannot figure out how to do this properly though.

The second handler is to insert text into the textbox. The code should be self-explanatory. The x-custom-offset property we set earlier is used here to insert the text into the correct place in the textbox. $('#dropdown').on( 'click', 'li a', function( e ) { ... } ); will attach the click event to the ul instead of the li's, so that it will keep working if you dynamically create the li's (but it will only fire if you click the link part).

这篇关于我如何将一个下拉菜单放置在文本区域内的光标位置?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆