wiem, że to nie jest dokładną odpowiedź na pytanie (rozwiązanie to nie korzysta z textarea, ale contentEditable div), ale nie sądzę, istnieje jakikolwiek sposób na uzyskanie współrzędnych XY za pomocą zdarzenia, atrybutu lub funkcji w obszarze tekstowym lub atrybutu lub funkcji w obiekcie Selection.
Mam oczkach przykład on JSBin. Należy pamiętać, że nie przejmowałem się testowaniem zgodności w innych przeglądarkach i że nie zwróci to uwagi do miejsca, w którym zostało przerwane. Nie mogę wymyślić kodu na ten temat. Wierzę, że window.getSelection()
nie będzie działać w IE, aw IE8 - byłoby zupełnie inaczej. Prawdopodobnie chcesz się upewnić, że menu nie będzie wyświetlane bezpośrednio z krawędzi ekranu.
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;
}
kodu 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;
});
});
Wyjaśnienie
Ten przykład działa w oparciu o które można wstawić element w contentEditable i odzyskać to przesunięcie do góry i po lewej stronie ekranu. Po naciśnięciu klawisza Shift + 50, procedura obsługi zdarzenia uniemożliwi zapisanie @, a zamiast tego wstawi sam obiekt atrapa @. Następnie pobieramy offset z tego obiektu i przesuwamy menu rozwijane do tego offsetu. Co więcej, zapisujemy offset znaków jako niestandardową właściwość menu, dzięki czemu możemy wstawić wartość w tej konkretnej lokalizacji. Następnie musimy usunąć dummy div, ale jeśli usuniemy manekina z $('#dummy').remove()
, węzeł tekstowy przed manekinem i węzeł tekstowy za atrapą nie będzie się scalał. Spowoduje to skasowanie ostatniego węzła tekstowego, jeśli chcemy umieścić inny @ w innym miejscu i/lub umieścić go w niewłaściwym miejscu. Dlatego po prostu ponownie zamieniamy zawartość edytowalnego div. Na koniec, karetka musi zostać przywrócona do pierwotnej pozycji. Nie wiem, jak to zrobić właściwie.
Drugi moduł obsługi polega na wstawianiu tekstu do pola tekstowego.Kod powinien być oczywisty. Ustawiona wcześniej właściwość x-custom-offset
służy do wstawiania tekstu w odpowiednim miejscu w polu tekstowym. $('#dropdown').on('click', 'li a', function(e) { ... });
załączy zdarzenie click do ul
zamiast li
, aby działało, jeśli dynamicznie utworzysz li
(ale będzie ono uruchamiane tylko wtedy, gdy klikniesz link).
Nie mogę wymyślić, jak to zrobić w polu tekstowym, ponieważ nie można wstawić elementu w polu tekstowym. Możliwe jest użycie elementu div z 'contentEditable =" true "' i jakoś sformatować go tak, aby wyglądał jak pole tekstowe. Możesz określić offset za pomocą selectionStart, a następnie zastąp 'abcd [cursor] efgh' opcją' abcd
Tak, próbowałem z contenteditable div ale dostałem uderzenie w wstawianiu innego div w pozycji kursora. Próbuję wstawić div, gdy wpisuję "@" w pozycji kursora i nie znalazłem sposobu, aby przejść ... Using .append() nie wstawi div w pozycji kursora, gdy wpisuję "@" gdziekolwiek w środku http://jsbin.com/uyufiw/25/edit – selvagsz
'$ ('# cel'). selectionStart' lub coś podobnego może być użyte do znalezienia przesunięcia kursora, którego możesz użyć do utworzenia dwa podciągi. Jeden od początku do kursora, a drugi od kursora do końca. Możesz następnie zastąpić zawartość '$ ('# target')' 'firststring + yourdiv + laststring'. Aby wstawić coś, możesz zastąpić '
' z wybraną opcją. (To jest po prostu pół-składnia, ale coś w stylu 'this.parent.outerHTML = this.value' na li-elemencie.) Przykro mi, ale nie mogę naprawdę przetestować na tym komputerze, więc nie mogę tego zrobić dużo, ale daje tajemnicze wskazówki. – Sumurai8