テキストエリア内のカーソル位置にドロップダウンを配置するにはどうすればよいですか? この質問はすでにここで何度も尋ねられていることがわかりましたが、正しい解決策を見つけることができません..
これはJSBINです
あなたの提案を手伝ってください
前もって感謝します
私はそれが質問に対する正確な答えではないことを知っています(この解決策はテキストエリアを使用しませんが、contentEditable divを使用します)が、イベント、属性のいずれかを使用してxy座標を取得する方法はないと思いますまたは textarea の関数、または Selection オブジェクトの属性または関数。
私はJSBinの例をまとめました。他のブラウザーでの互換性をわざわざテストしていないことと、中断した場所にキャレットが戻らないことに注意してください。そのためのコードがわかりません。IEでは機能しないと思いますwindow.getSelection()
が、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 の内容を再度置き換えるだけです。最後に、キャレットを元の位置に戻す必要があります。ただし、これを適切に行う方法がわかりません。
2 番目のハンドラーは、テキスト ボックスにテキストを挿入することです。コードは自明である必要があります。ここx-custom-offset
では、前に設定したプロパティを使用して、テキスト ボックスの正しい場所にテキストを挿入します。はクリック イベントを の代わりに に$('#dropdown').on( 'click', 'li a', function( e ) { ... } );
アタッチするので、 を動的に作成しても動作し続けます(ただし、リンク部分をクリックした場合にのみ発生します)。ul
li
li
マウスの位置を取得して、ドロップダウン リストをこの位置に移動できます。ポップアップ コンテンツの 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>