解决方法
我知道这不是问题的确切答案(这个解决方案不使用textarea,而是使用contentEditable div),但我认为没有任何方法可以使用事件,属性来获取xy坐标或者在textarea上运行或者在Selection对象上使用属性或函数.
我已经编写了一个示例on 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时,事件处理程序将阻止写入@,而是插入@ dummy对象本身.然后我们从该对象检索偏移量并将下拉菜单移动到该偏移量.此外,我们将字符偏移量保存为菜单的自定义属性x-custom-offset,以便我们可以在该特定位置插入值.然后我们需要删除虚拟div,但是如果我们用$(‘#dummy’)删除虚拟对象.在虚拟之前删除()文本节点,虚拟对象后面的文本节点将不会合并.这将删除最后一个textnode,如果我们要在其他地方放置一个@和/或将它放在错误的位置.因此,我们只需再次替换可编辑div的内容.最后,插入符号必须回到它的原始位置.我无法弄清楚如何正确地做到这一点.
第二个处理程序是将文本插入文本框.代码应该是不言自明的.我们之前设置的x-custom-offset属性用于将文本插入文本框中的正确位置. $(‘#dropdown’).on(‘click’,’li a’,function(e){…});将click事件附加到ul而不是li,以便在动态创建li时它将继续工作(但只有在单击链接部分时才会触发).