텍스트 영역의 커서 위치에 DIV 표시
이 질문에 이미 답변이 있습니다.
내 프로젝트의 경우 특정 텍스트 영역에 대한 자동 완성 기능을 제공하고 싶습니다. intellisense / omnicomplete가 작동하는 방식과 유사합니다. 그러나이를 위해 절대 커서 위치를 찾아야 DIV가 어디에 표시되어야하는지 알 수 있습니다.
밝혀졌다 : 그것은 (거의 나는 희망한다) 달성하기 불가능하다. 누구든지 그 문제를 해결하는 방법에 대한 깔끔한 아이디어가 있습니까?
내 Hacky 실험 버전 2
이 새 버전은 모든 글꼴과 함께 작동하며 필요에 따라 조정할 수 있으며 모든 텍스트 영역 크기를 조정할 수 있습니다.
여러분 중 일부가 여전히이 기능을 작동 시키려고 노력하고 있음을 알게 된 후 저는 새로운 접근 방식을 시도하기로 결정했습니다. 내 결과는 이번에는 훨씬 더 나아졌습니다-적어도 Linux의 Google 크롬에서는. 더 이상 Windows PC를 사용할 수 없으므로 Ubuntu의 chrome / firefox에서만 테스트 할 수 있습니다. 내 결과는 Chrome에서 100 % 일관되게 작동하고 Firefox에서 약 70-80 % 정도라고 가정 해 봅시다.하지만 불일치를 찾는 것이 엄청나게 어려울 것이라고 생각하지 않습니다.
이 새 버전은 Canvas 개체를 사용합니다. 제 예 에서는 실제로 캔버스를 보여주었습니다. 실제로 볼 수 있도록 표시했지만 숨겨진 캔버스 개체를 사용하면 매우 쉽게 수행 할 수 있습니다.
이것은 가장 확실한 해킹이며, 오히려 함께 던져진 코드에 대해 미리 사과드립니다. 최소한 Google 크롬에서는 내가 설정 한 글꼴이나 텍스트 영역의 크기에 관계없이 일관되게 작동합니다. 내가 사용하는 샘 사프란 커서 좌표 (회색 배경 DIV)를 보여 '의 예를. 또한 "Randomize"링크를 추가하여 다양한 글꼴 / 텍사스 영역 크기 및 스타일로 작동하는 것을 볼 수 있으며 커서 위치 업데이트를 즉시 볼 수 있습니다. 컴패니언 캔버스가 함께 재생되는 것을 더 잘 볼 수 있도록 전체 페이지 데모를 보는 것이 좋습니다 .
어떻게 작동하는지 요약하겠습니다 ...
기본 아이디어는 가능한 한 가깝게 캔버스에 텍스트 영역을 다시 그리려고한다는 것입니다. 브라우저는 및 texarea 모두에 대해 동일한 글꼴 엔진을 사용하기 때문에 캔버스의 글꼴 측정 기능을 사용하여 사물이 어디에 있는지 파악할 수 있습니다. 거기에서 사용 가능한 캔버스 메서드를 사용하여 좌표를 알아낼 수 있습니다.
무엇보다 먼저 캔버스를 텍스트 영역의 크기에 맞게 조정합니다. 캔버스 크기가 결과에 실제로 영향을 미치지 않기 때문에 이것은 전적으로 시각적 목적을위한 것입니다. Canvas는 실제로 단어 줄 바꿈 수단을 제공하지 않기 때문에 텍스트 영역과 최대한 일치시키기 위해 줄을 나누는 수단 (함께 훔치거나 빌리거나 멍청이)을 고안해야했습니다. 브라우저 간 조정을 가장 많이해야하는 곳입니다.
단어 줄 바꿈 후 다른 모든 것은 기본 수학입니다. 줄 바꿈을 모방하기 위해 줄을 배열로 분할하고 이제 해당 줄을 반복하고 현재 선택이 끝나는 지점까지 아래로 내려갑니다. 그렇게하기 위해 우리는 문자를 세는 중이고을 능가 selection.end
하면 충분히 멀리 내려 갔다는 것을 압니다. 라인 높이와 그 지점까지 라인 수를 곱하면 y
좌표가 있습니다.
x
좌표는 우리가 사용하고있는 것을 제외하고, 매우 유사하다 context.measureText
. 올바른 수의 문자를 인쇄하는 한 Canvas에 그려지는 선의 너비를 제공합니다. 이것은 마지막으로 기록 된 문자 (현재 selection.end
위치 이전의 문자) 이후에 끝나게 됩니다.
다른 브라우저에서 이것을 디버깅하려고 할 때 찾아야 할 것은 줄이 제대로 끊어지지 않는 부분입니다. 캔버스의 한 줄에있는 마지막 단어가 텍스트 영역에서 줄 바꿈되거나 그 반대의 경우 일부 위치에서 볼 수 있습니다. 이것은 브라우저가 단어 줄 바꿈을 처리하는 방법과 관련이 있습니다. 캔버스에서 텍스트 영역과 일치하도록 줄 바꿈을하는 한 커서는 정확해야합니다.
아래에 소스를 붙여 넣겠습니다. 복사하여 붙여 넣을 수 있어야하지만 그렇게한다면 내 서버에있는 jquery-fieldselection 사본을 직접 다운로드하는 대신 다운로드 해 주시기 바랍니다.
나는 또한 바이올린 뿐만 아니라 새로운 데모 를 올렸다 .
행운을 빕니다!
<!DOCTYPE html>
<html lang="en-US">
<head>
<meta charset="utf-8" />
<title>Tooltip 2</title>
<script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
<script type="text/javascript" src="http://enobrev.info/cursor/js/jquery-fieldselection.js"></script>
<style type="text/css">
form {
float: left;
margin: 20px;
}
#textariffic {
height: 400px;
width: 300px;
font-size: 12px;
font-family: 'Arial';
line-height: 12px;
}
#tip {
width:5px;
height:30px;
background-color: #777;
position: absolute;
z-index:10000
}
#mock-text {
float: left;
margin: 20px;
border: 1px inset #ccc;
}
/* way the hell off screen */
.scrollbar-measure {
width: 100px;
height: 100px;
overflow: scroll;
position: absolute;
top: -9999px;
}
#randomize {
float: left;
display: block;
}
</style>
<script type="text/javascript">
var oCanvas;
var oTextArea;
var $oTextArea;
var iScrollWidth;
$(function() {
iScrollWidth = scrollMeasure();
oCanvas = document.getElementById('mock-text');
oTextArea = document.getElementById('textariffic');
$oTextArea = $(oTextArea);
$oTextArea
.keyup(update)
.mouseup(update)
.scroll(update);
$('#randomize').bind('click', randomize);
update();
});
function randomize() {
var aFonts = ['Arial', 'Arial Black', 'Comic Sans MS', 'Courier New', 'Impact', 'Times New Roman', 'Verdana', 'Webdings'];
var iFont = Math.floor(Math.random() * aFonts.length);
var iWidth = Math.floor(Math.random() * 500) + 300;
var iHeight = Math.floor(Math.random() * 500) + 300;
var iFontSize = Math.floor(Math.random() * 18) + 10;
var iLineHeight = Math.floor(Math.random() * 18) + 10;
var oCSS = {
'font-family': aFonts[iFont],
width: iWidth + 'px',
height: iHeight + 'px',
'font-size': iFontSize + 'px',
'line-height': iLineHeight + 'px'
};
console.log(oCSS);
$oTextArea.css(oCSS);
update();
return false;
}
function showTip(x, y) {
$('#tip').css({
left: x + 'px',
top: y + 'px'
});
}
// https://stackoverflow.com/a/11124580/14651
// https://stackoverflow.com/a/3960916/14651
function wordWrap(oContext, text, maxWidth) {
var aSplit = text.split(' ');
var aLines = [];
var sLine = "";
// Split words by newlines
var aWords = [];
for (var i in aSplit) {
var aWord = aSplit[i].split('\n');
if (aWord.length > 1) {
for (var j in aWord) {
aWords.push(aWord[j]);
aWords.push("\n");
}
aWords.pop();
} else {
aWords.push(aSplit[i]);
}
}
while (aWords.length > 0) {
var sWord = aWords[0];
if (sWord == "\n") {
aLines.push(sLine);
aWords.shift();
sLine = "";
} else {
// Break up work longer than max width
var iItemWidth = oContext.measureText(sWord).width;
if (iItemWidth > maxWidth) {
var sContinuous = '';
var iWidth = 0;
while (iWidth <= maxWidth) {
var sNextLetter = sWord.substring(0, 1);
var iNextWidth = oContext.measureText(sContinuous + sNextLetter).width;
if (iNextWidth <= maxWidth) {
sContinuous += sNextLetter;
sWord = sWord.substring(1);
}
iWidth = iNextWidth;
}
aWords.unshift(sContinuous);
}
// Extra space after word for mozilla and ie
var sWithSpace = (jQuery.browser.mozilla || jQuery.browser.msie) ? ' ' : '';
var iNewLineWidth = oContext.measureText(sLine + sWord + sWithSpace).width;
if (iNewLineWidth <= maxWidth) { // word fits on current line to add it and carry on
sLine += aWords.shift() + " ";
} else {
aLines.push(sLine);
sLine = "";
}
if (aWords.length === 0) {
aLines.push(sLine);
}
}
}
return aLines;
}
// http://davidwalsh.name/detect-scrollbar-width
function scrollMeasure() {
// Create the measurement node
var scrollDiv = document.createElement("div");
scrollDiv.className = "scrollbar-measure";
document.body.appendChild(scrollDiv);
// Get the scrollbar width
var scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth;
// Delete the DIV
document.body.removeChild(scrollDiv);
return scrollbarWidth;
}
function update() {
var oPosition = $oTextArea.position();
var sContent = $oTextArea.val();
var oSelection = $oTextArea.getSelection();
oCanvas.width = $oTextArea.width();
oCanvas.height = $oTextArea.height();
var oContext = oCanvas.getContext("2d");
var sFontSize = $oTextArea.css('font-size');
var sLineHeight = $oTextArea.css('line-height');
var fontSize = parseFloat(sFontSize.replace(/[^0-9.]/g, ''));
var lineHeight = parseFloat(sLineHeight.replace(/[^0-9.]/g, ''));
var sFont = [$oTextArea.css('font-weight'), sFontSize + '/' + sLineHeight, $oTextArea.css('font-family')].join(' ');
var iSubtractScrollWidth = oTextArea.clientHeight < oTextArea.scrollHeight ? iScrollWidth : 0;
oContext.save();
oContext.clearRect(0, 0, oCanvas.width, oCanvas.height);
oContext.font = sFont;
var aLines = wordWrap(oContext, sContent, oCanvas.width - iSubtractScrollWidth);
var x = 0;
var y = 0;
var iGoal = oSelection.end;
aLines.forEach(function(sLine, i) {
if (iGoal > 0) {
oContext.fillText(sLine.substring(0, iGoal), 0, (i + 1) * lineHeight);
x = oContext.measureText(sLine.substring(0, iGoal + 1)).width;
y = i * lineHeight - oTextArea.scrollTop;
var iLineLength = sLine.length;
if (iLineLength == 0) {
iLineLength = 1;
}
iGoal -= iLineLength;
} else {
// after
}
});
oContext.restore();
showTip(oPosition.left + x, oPosition.top + y);
}
</script>
</head>
<body>
<a href="#" id="randomize">Randomize</a>
<form id="tipper">
<textarea id="textariffic">Aliquam urna. Nullam augue dolor, tincidunt condimentum, malesuada quis, ultrices at, arcu. Aliquam nunc pede, convallis auctor, sodales eget, aliquam eget, ligula. Proin nisi lacus, scelerisque nec, aliquam vel, dictum mattis, eros. Curabitur et neque. Fusce sollicitudin. Quisque at risus. Suspendisse potenti. Mauris nisi. Sed sed enim nec dui viverra congue. Phasellus velit sapien, porttitor vitae, blandit volutpat, interdum vel, enim. Cras sagittis bibendum neque. Proin eu est. Fusce arcu. Aliquam elit nisi, malesuada eget, dignissim sed, ultricies vel, purus. Maecenas accumsan diam id nisi.
Phasellus et nunc. Vivamus sem felis, dignissim non, lacinia id, accumsan quis, ligula. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Sed scelerisque nulla sit amet mi. Nulla consequat, elit vitae tempus vulputate, sem libero rhoncus leo, vulputate viverra nulla purus nec turpis. Nam turpis sem, tincidunt non, congue lobortis, fermentum a, ipsum. Nulla facilisi. Aenean facilisis. Maecenas a quam eu nibh lacinia ultricies. Morbi malesuada orci quis tellus.
Sed eu leo. Donec in turpis. Donec non neque nec ante tincidunt posuere. Pellentesque blandit. Ut vehicula vestibulum risus. Maecenas commodo placerat est. Integer massa nunc, luctus at, accumsan non, pulvinar sed, odio. Pellentesque eget libero iaculis dui iaculis vehicula. Curabitur quis nulla vel felis ullamcorper varius. Sed suscipit pulvinar lectus.</textarea>
</form>
<div id="tip"></div>
<canvas id="mock-text"></canvas>
</body>
</html>
곤충
제가 기억하는 버그가 하나 있습니다. 줄의 첫 글자 앞에 커서를 놓으면 이전 줄의 마지막 글자로 "위치"가 표시됩니다. 이것은 selection.end가 작동하는 방식과 관련이 있습니다. 나는 그 사건을 찾아서 그에 따라 고치는 것이 너무 어렵지 않을 것이라고 생각합니다.
버전 1
편집 내역을 파헤 치지 않고도 진행 상황을 볼 수 있도록 여기에 두십시오.
완벽하지 않고 가장 확실한 해킹이지만 WinXP IE, FF, Safari, Chrome 및 Opera에서 꽤 잘 작동합니다.
내가 말할 수있는 한 모든 브라우저에서 커서의 x / y를 직접 찾을 수있는 방법은 없습니다. IE 방법 , 언급 에 의해 아담 벨레는 불행하게도 크로스 브라우저하지 흥미롭지 만. 차선책은 문자를 그리드로 사용하는 것입니다.
안타깝게도 모든 브라우저에 내장 된 글꼴 메트릭 정보가 없습니다. 즉, 고정 폭 글꼴이 일관된 측정 값을 갖는 유일한 글꼴 유형임을 의미합니다. 또한 글꼴 높이에서 글꼴 너비를 알아낼 수있는 신뢰할 수있는 방법이 없습니다. 처음에는 높이의 비율을 사용해 보았습니다. 그런 다음 글꼴 크기를 변경하고 모든 것이 지옥으로갔습니다.
임시 텍스트 영역을 만들고 scrollHeight (또는 scrollWidth)가 변경 될 때까지 문자를 계속 추가하는 문자 너비를 알아내는 한 가지 방법을 시도했습니다. 그럴듯 해 보이지만, 그 길 중간 쯤에 텍스트 영역에서 cols 속성을 사용할 수 있다는 것을 깨달았고이 시련에 다른 하나를 추가 할 수있는 충분한 해킹이 있다고 생각했습니다. 이것은 CSS를 통해 텍스트 영역의 너비를 설정할 수 없음을 의미합니다. 이 작업을 수행하려면 cols를 사용해야합니다.
다음 문제는 CSS를 통해 글꼴을 설정하더라도 브라우저가 글꼴을 다르게보고한다는 것입니다. 글꼴을 설정하지 않으면 mozilla는 monospace
기본적으로 사용 하고 IE는 Courier New
, Opera "Courier New"
(따옴표 포함), Safari 'Lucida Grand'
(작은 따옴표 포함)를 사용합니다. 글꼴을 monospace
, mozilla로 설정하면 즉, 제공 한 것을 가져 오면 Safari가 그대로 나오고 -webkit-monospace
Opera는 그대로 유지됩니다 "Courier New"
.
이제 우리는 일부 변수를 초기화합니다. CSS에서도 줄 높이를 설정하십시오. Firefox는 올바른 줄 높이를보고하지만 IE는 "정상"으로보고했고 다른 브라우저에서는 신경 쓰지 않았습니다. 내 CSS에 줄 높이를 설정하고 그 차이를 해결했습니다. 픽셀 대신 ems를 사용하여 테스트하지 않았습니다. 문자 높이는 글꼴 크기입니다. 아마도 CSS에서도 미리 설정해야합니다.
또한 캐릭터 배치를 시작하기 전에 한 번 더 사전 설정을했습니다. 이로 인해 머리를 긁적입니다. ie 및 mozilla의 경우 texarea 문자는 <cols이고 다른 모든 문자는 <= char입니다. 따라서 Chrome은 50 자까지 맞출 수 있지만, mozilla 및 즉, 줄에서 마지막 단어를 분리합니다.
이제 모든 줄에 대한 첫 번째 문자 위치 배열을 만들 것입니다. 텍스트 영역의 모든 문자를 반복합니다. 개행 인 경우 라인 배열에 새 위치를 추가합니다. 공백이면 현재 "단어"가 현재 진행중인 줄에 맞는지 아니면 다음 줄로 밀려 날지 알아 내려고합니다. 구두점은 "단어"의 일부로 간주됩니다. 탭으로 테스트하지 않았지만 탭 문자에 4 개의 문자를 추가하는 줄이 있습니다.
줄 위치의 배열이 있으면 반복해서 커서가있는 줄을 찾습니다. 선택의 "End"를 커서로 사용하고 있습니다.
x = (커서 위치-커서 라인의 첫 번째 문자 위치) * 문자 너비
y = ((커서 라인 + 1) * 라인 높이)-스크롤 위치
내가 사용하고 JQuery와 1.2.6 , JQuery와 - fieldselection 및 JQuery와 차원을
데모 : http://enobrev.info/cursor/
그리고 코드 :
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Tooltip</title>
<script type="text/javascript" src="js/jquery-1.2.6.js"></script>
<script type="text/javascript" src="js/jquery-fieldselection.js"></script>
<script type="text/javascript" src="js/jquery.dimensions.js"></script>
<style type="text/css">
form {
margin: 20px auto;
width: 500px;
}
#textariffic {
height: 400px;
font-size: 12px;
font-family: monospace;
line-height: 15px;
}
#tip {
position: absolute;
z-index: 2;
padding: 20px;
border: 1px solid #000;
background-color: #FFF;
}
</style>
<script type="text/javascript">
$(function() {
$('textarea')
.keyup(update)
.mouseup(update)
.scroll(update);
});
function showTip(x, y) {
y = y + $('#tip').height();
$('#tip').css({
left: x + 'px',
top: y + 'px'
});
}
function update() {
var oPosition = $(this).position();
var sContent = $(this).val();
var bGTE = jQuery.browser.mozilla || jQuery.browser.msie;
if ($(this).css('font-family') == 'monospace' // mozilla
|| $(this).css('font-family') == '-webkit-monospace' // Safari
|| $(this).css('font-family') == '"Courier New"') { // Opera
var lineHeight = $(this).css('line-height').replace(/[^0-9]/g, '');
lineHeight = parseFloat(lineHeight);
var charsPerLine = this.cols;
var charWidth = parseFloat($(this).innerWidth() / charsPerLine);
var iChar = 0;
var iLines = 1;
var sWord = '';
var oSelection = $(this).getSelection();
var aLetters = sContent.split("");
var aLines = [];
for (var w in aLetters) {
if (aLetters[w] == "\n") {
iChar = 0;
aLines.push(w);
sWord = '';
} else if (aLetters[w] == " ") {
var wordLength = parseInt(sWord.length);
if ((bGTE && iChar + wordLength >= charsPerLine)
|| (!bGTE && iChar + wordLength > charsPerLine)) {
iChar = wordLength + 1;
aLines.push(w - wordLength);
} else {
iChar += wordLength + 1; // 1 more char for the space
}
sWord = '';
} else if (aLetters[w] == "\t") {
iChar += 4;
} else {
sWord += aLetters[w];
}
}
var iLine = 1;
for(var i in aLines) {
if (oSelection.end < aLines[i]) {
iLine = parseInt(i) - 1;
break;
}
}
if (iLine > -1) {
var x = parseInt(oSelection.end - aLines[iLine]) * charWidth;
} else {
var x = parseInt(oSelection.end) * charWidth;
}
var y = (iLine + 1) * lineHeight - this.scrollTop; // below line
showTip(oPosition.left + x, oPosition.top + y);
}
}
</script>
</head>
<body>
<form id="tipper">
<textarea id="textariffic" cols="50">
Aliquam urna. Nullam augue dolor, tincidunt condimentum, malesuada quis, ultrices at, arcu. Aliquam nunc pede, convallis auctor, sodales eget, aliquam eget, ligula. Proin nisi lacus, scelerisque nec, aliquam vel, dictum mattis, eros. Curabitur et neque. Fusce sollicitudin. Quisque at risus. Suspendisse potenti. Mauris nisi. Sed sed enim nec dui viverra congue. Phasellus velit sapien, porttitor vitae, blandit volutpat, interdum vel, enim. Cras sagittis bibendum neque. Proin eu est. Fusce arcu. Aliquam elit nisi, malesuada eget, dignissim sed, ultricies vel, purus. Maecenas accumsan diam id nisi.
Phasellus et nunc. Vivamus sem felis, dignissim non, lacinia id, accumsan quis, ligula. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Sed scelerisque nulla sit amet mi. Nulla consequat, elit vitae tempus vulputate, sem libero rhoncus leo, vulputate viverra nulla purus nec turpis. Nam turpis sem, tincidunt non, congue lobortis, fermentum a, ipsum. Nulla facilisi. Aenean facilisis. Maecenas a quam eu nibh lacinia ultricies. Morbi malesuada orci quis tellus.
Sed eu leo. Donec in turpis. Donec non neque nec ante tincidunt posuere. Pellentesque blandit. Ut vehicula vestibulum risus. Maecenas commodo placerat est. Integer massa nunc, luctus at, accumsan non, pulvinar sed, odio. Pellentesque eget libero iaculis dui iaculis vehicula. Curabitur quis nulla vel felis ullamcorper varius. Sed suscipit pulvinar lectus.
</textarea>
</form>
<p id="tip">Here I Am!!</p>
</body>
</html>
이 문제와 관련된 주제를 러시아어 JavaScript 사이트에 게시했습니다.
러시아어를 이해하지 못하는 경우 Google 버전으로 번역 해보십시오. http://translate.google.ru/translate?js=y&prev=_t&hl=ru&ie=UTF-8&layout=1&eotf=1&u=http://javascript.ru/forum /events/7771-poluchit-koordinaty-kursora-v-tekstovom-pole-v-pikselyakh.html&sl=ru&tl=ko
번역 된 버전의 코드 예제에는 몇 가지 마크 업 문제가 있으므로 원래 러시아어 게시물에서 코드를 읽을 수 있습니다 .
아이디어는 간단합니다. 커서 위치를 픽셀 단위로 가져 오는 쉽고 보편적이며 브라우저 간 방법은 없습니다. 솔직히 말해서 Internet Explorer에만 해당됩니다.
다른 브라우저에서 정말 계산해야한다면 ...
- 보이지 않는 DIV 만들기
- 텍스트 상자의 모든 스타일과 내용을 해당 DIV에 복사
- 그런 다음 텍스트 상자에서 캐럿이있는 텍스트의 동일한 위치에 HTML 요소를 삽입합니다.
- 해당 HTML 요소의 좌표를 가져옵니다.
이 문제와 관련된 문제는 다른 게시물에서 잘 설명되어 있으므로 다시 설명하지 않겠습니다. 가능한 해결책을 가리킬 것이며 버그가 있지만 시작점입니다.
다행히 Github에 컨테이너와 관련된 캐럿 위치를 계산하는 스크립트가 있지만 jQuery가 필요합니다. GitHub 페이지 : jquery-caret-position-getter, Thanxs to Bevis.Zhao.
이를 기반으로 다음 코드를 구현 했습니다. jsFiddle.net에서 여기에서 확인 하십시오.
<html><head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<title>- jsFiddle demo by mjerez</title>
<script type="text/javascript" src="http://code.jquery.com/jquery-1.8.2.js"></script>
<link rel="stylesheet" type="text/css" href="http://jsfiddle.net/css/normalize.css">
<link rel="stylesheet" type="text/css" href="http://jsfiddle.net/css/result-light.css">
<script type="text/javascript" src="https://raw.github.com/beviz/jquery-caret-position-getter/master/jquery.caretposition.js"></script>
<style type="text/css">
body{position:relative;font:normal 100% Verdana, Geneva, sans-serif;padding:10px;}
.aux{background:#ccc;opacity: 0.5;width:50%;padding:5px;border:solid 1px #aaa;}
.hidden{display:none}
.show{display:block; position:absolute; top:0px; left:0px;}
</style>
<script type="text/javascript">//<![CDATA[
$(document).keypress(function(e) {
if ($(e.target).is('input, textarea')) {
var key = String.fromCharCode(e.which);
var ctrl = e.ctrlKey;
if (ctrl) {
var display = $("#autocomplete");
var editArea = $('#editArea');
var pos = editArea.getCaretPosition();
var offset = editArea.offset();
// now you can use left, top(they are relative position)
display.css({
left: offset.left + pos.left,
top: offset.top + pos.top,
color : "#449"
})
display.toggleClass("show");
return false;
}
}
});
window.onload = (function() {
$("#editArea").blur(function() {
if ($("#autocomplete").hasClass("show")) $("#autocomplete").toggleClass("show");
})
});
//]]>
</script>
</head>
<body>
<p>Click ctrl+space to while you write to diplay the autocmplete pannel.</p>
</br>
<textarea id="editArea" rows="4" cols="50"></textarea>
</br>
</br>
</br>
<div id="autocomplete" class="aux hidden ">
<ol>
<li>Option a</li>
<li>Option b</li>
<li>Option c</li>
<li>Option d</li>
</ol>
</div>
</body>
이 질문은 한 달 전에 요청한 질문과 중복되며 여기에 답변했습니다 . 이 질문은 몇 년 전에 중복으로 닫혔어야했기 때문에 해당 링크에서만 답변을 유지할 것입니다.
답변 사본
meteor-autocomplete에 대한 textarea caret coordinates 플러그인을 찾았 으므로 GitHub에서 8 개 플러그인을 모두 평가했습니다. 승자는 Component의 textarea-caret-position 입니다 .
풍모
- 픽셀 정밀도
- 전혀 의존성 없음
- 브라우저 호환성 : Chrome, Safari, Firefox ( 두 가지 버그 에도 불구하고 ), IE9 +; 작동하지만 Opera, IE8 또는 이전 버전에서는 테스트되지 않았습니다.
- 모든 글꼴 패밀리 및 크기, 텍스트 변환 지원
- 텍스트 영역에는 임의의 패딩 또는 테두리가있을 수 있습니다.
- 텍스트 영역의 가로 또는 세로 스크롤바로 혼동되지 않음
- 하드 리턴, 탭 (IE 제외) 및 텍스트의 연속 공백 지원
- 텍스트 영역의 열보다 긴 줄의 올바른 위치
- 긴 단어를 줄 바꿈 할 때 줄 끝의 빈 공간 에 "고스트"위치가 없음
다음은 데모입니다-http: //jsfiddle.net/dandv/aFPA7/
작동 원리
거울 <div>
은 화면 밖에서 생성되고 <textarea>
. 그런 다음 캐럿까지 텍스트 영역의 텍스트가 div에 복사되고 <span>
바로 뒤에 a 가 삽입됩니다. 그런 다음 스팬의 텍스트 콘텐츠는 가짜 div의 래핑을 충실하게 재현하기 위해 텍스트 영역의 나머지 텍스트로 설정됩니다.
이것은 긴 줄을 감싸는 것과 관련된 모든 가장자리 케이스를 처리 할 수있는 유일한 방법입니다. 또한 GitHub에서 @ 사용자 드롭 다운 의 위치를 결정하는 데 사용됩니다 .
이 블로그 는 질문에 너무 가깝게 대답하는 것 같습니다. 나는 직접 시도하지 않았지만 저자는 FF3, Chrome, IE, Opera, Safari에서 테스트했다고 말합니다. 코드는 GitHub 에 있습니다.
여기에서 수정했습니다 : http://jsfiddle.net/eMwKd/4/
유일한 단점은 이미 제공된 기능이 getCaret()
키를 누를 때 잘못된 위치로 해결된다는 것입니다. 따라서 키를 놓지 않으면 빨간색 커서가 실제 커서 뒤에있는 것처럼 보입니다.
나는 그것에 대해 다시 살펴볼 것입니다.
업데이트 : 흠, 줄이 너무 길면 줄 바꿈이 정확하지 않습니다 ..
이 블로그 게시물은 귀하의 질문을 해결하는 것 같지만 불행히도 저자는 IE 6에서만 테스트했다고 인정합니다.
IE의 DOM은 문자의 상대적 위치에 대한 정보를 제공하지 않습니다. 그러나 브라우저에서 렌더링 된 컨트롤에 대한 경계 및 오프셋 값을 제공합니다. 따라서이 값을 사용하여 캐릭터의 상대적 경계를 결정했습니다. 그런 다음 JavaScript TextRange를 사용하여 주어진 TextArea 내의 고정 너비 글꼴에 대한 선 및 열 위치를 계산하는 이러한 측정 값을 사용하는 메커니즘을 만들었습니다.
먼저 TextArea의 상대 경계는 사용 된 고정 너비 글꼴의 크기를 기반으로 계산되어야합니다. 이렇게하려면 TextArea의 원래 값을 로컬 JavaScript 변수에 저장하고 값을 지워야합니다. 그런 다음 TextArea의 위쪽 및 왼쪽 경계를 결정하기 위해 TextRange가 만들어집니다.
아마도 이것은 당신을 기쁘게 할 것입니다, 그것은 선택의 위치와 커서의 위치를 알려줄 것이므로 자동 위치를 얻기 위해 타이머를 확인하거나 선택 가져 오기 버튼을 클릭하여 위치를 얻으려면 선택을 취소하십시오
<form>
<p>
<input type="button" onclick="evalOnce();" value="Get Selection">
timer:
<input id="eval_switch" type="checkbox" onclick="evalSwitchClicked(this)">
<input id="eval_time" type="text" value="200" size="6">
ms
</p>
<textarea id="code" cols="50" rows="20">01234567890123456789012345678901234567890123456789 01234567890123456789012345678901234567890123456789 01234567890123456789012345678901234567890123456789 01234567890123456789012345678901234567890123456789 01234567890123456789012345678901234567890123456789 Sample text area. Please select above text. </textarea>
<textarea id="out" cols="50" rows="20"></textarea>
</form>
<div id="test"></div>
<script>
function Selection(textareaElement) {
this.element = textareaElement;
}
Selection.prototype.create = function() {
if (document.selection != null && this.element.selectionStart == null) {
return this._ieGetSelection();
} else {
return this._mozillaGetSelection();
}
}
Selection.prototype._mozillaGetSelection = function() {
return {
start: this.element.selectionStart,
end: this.element.selectionEnd
};
}
Selection.prototype._ieGetSelection = function() {
this.element.focus();
var range = document.selection.createRange();
var bookmark = range.getBookmark();
var contents = this.element.value;
var originalContents = contents;
var marker = this._createSelectionMarker();
while(contents.indexOf(marker) != -1) {
marker = this._createSelectionMarker();
}
var parent = range.parentElement();
if (parent == null || parent.type != "textarea") {
return { start: 0, end: 0 };
}
range.text = marker + range.text + marker;
contents = this.element.value;
var result = {};
result.start = contents.indexOf(marker);
contents = contents.replace(marker, "");
result.end = contents.indexOf(marker);
this.element.value = originalContents;
range.moveToBookmark(bookmark);
range.select();
return result;
}
Selection.prototype._createSelectionMarker = function() {
return "##SELECTION_MARKER_" + Math.random() + "##";
}
var timer;
var buffer = "";
function evalSwitchClicked(e) {
if (e.checked) {
evalStart();
} else {
evalStop();
}
}
function evalStart() {
var o = document.getElementById("eval_time");
timer = setTimeout(timerHandler, o.value);
}
function evalStop() {
clearTimeout(timer);
}
function timerHandler() {
clearTimeout(timer);
var sw = document.getElementById("eval_switch");
if (sw.checked) {
evalOnce();
evalStart();
}
}
function evalOnce() {
try {
var selection = new Selection(document.getElementById("code"));
var s = selection.create();
var result = s.start + ":" + s.end;
buffer += result;
flush();
} catch (ex) {
buffer = ex;
flush();
}
}
function getCode() {
// var s.create()
// return document.getElementById("code").value;
}
function clear() {
var out = document.getElementById("out");
out.value = "";
}
function print(str) {
buffer += str + "\n";
}
function flush() {
var out = document.getElementById("out");
out.value = buffer;
buffer = "";
}
</script>
여기 데모보기 : jsbin.com
나는에 대한 해결책을 알고하지 않습니다 textarea
하지만 확실히 작동 div
과 함께 contenteditable
.
Range
API 를 사용할 수 있습니다 . 이렇게 : (예,이 3 줄의 코드 만 필요합니다)
// get active selection
var selection = window.getSelection();
// get the range (you might want to check selection.rangeCount
// to see if it's popuplated)
var range = selection.getRangeAt(0);
// will give you top, left, width, height
console.log(range.getBoundingClientRect());
브라우저 호환성에 대해서는 잘 모르겠지만 최신 Chrome, Firefox 및 IE7에서도 작동하는 것으로 나타났습니다 (7 개를 테스트 한 것 같으면 9 개였습니다).
다음과 같이 '미친'일을 할 수도 있습니다. 입력 중이고 "#hash"
커서가 마지막 h
에 있으면 현재 범위에서 #
문자 를 찾고 범위를 문자 단위로 다시 이동하고 해당 범위 n
의 경계 사각형 을 가져올 수 있습니다. , 이렇게하면 popup-div가 단어에 '고착'된 것처럼 보입니다.
한 가지 사소한 단점은 contenteditable
때때로 약간 버그 가있을 수 있다는 것입니다. 커서는 불가능한 위치로 이동하는 것을 좋아하므로 이제 HTML 입력을 처리해야합니다. 그러나 브라우저 공급 업체는 이러한 문제를 더 많은 사이트에서 사용하기 시작할 것이라고 확신합니다.
제가 드릴 수있는 또 다른 팁은 rangy
도서관을 보세요 . 완전한 기능을 갖춘 교차 호환 범위 라이브러리가 되려고합니다. 필요 하지 않지만 오래된 브라우저를 다루는 경우 그만한 가치가있을 수 있습니다.
캐럿 오프셋에 대한 하나의 해킹에 대한 설명이 있습니다 : Textarea X / Y caret coordinates-jQuery 플러그인
또한 contenteditable
html5 기능을 사용할 수 있다면 속성 과 함께 div 요소를 사용하는 것이 좋습니다 .
복제 div에 span 요소를 추가하고이 범위의 오프셋을 기반으로 가짜 커서를 설정하는 것은 어떻습니까? 여기에서 바이올린을 업데이트 했습니다 . 또한 여기에 JS 비트 만 있습니다.
// http://stackoverflow.com/questions/263743/how-to-get-caret-position-in-textarea
var map = [];
var pan = '<span>|</span>'
//found @ http://davidwalsh.name/detect-scrollbar-width
function getScrollbarWidth() {
var scrollDiv = document.createElement("div");
scrollDiv.className = "scrollbar-measure";
document.body.appendChild(scrollDiv);
// Get the scrollbar width
var scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth;
// Delete the DIV
document.body.removeChild(scrollDiv);
return scrollbarWidth;
}
function getCaret(el) {
if (el.selectionStart) {
return el.selectionStart;
} else if (document.selection) {
el.focus();
var r = document.selection.createRange();
if (r == null) {
return 0;
}
var re = el.createTextRange(),
rc = re.duplicate();
re.moveToBookmark(r.getBookmark());
rc.setEndPoint('EndToStart', re);
return rc.text.length;
}
return 0;
}
$(function() {
var span = $('#pos span');
var textarea = $('textarea');
var note = $('#note');
css = getComputedStyle(document.getElementById('textarea'));
try {
for (i in css) note.css(css[i]) && (css[i] != 'width' && css[i] != 'height') && note.css(css[i], css.getPropertyValue(css[i]));
} catch (e) {}
note.css('max-width', '300px');
document.getElementById('note').style.visibility = 'hidden';
var height = note.height();
var fakeCursor, hidePrompt;
textarea.on('keyup click', function(e) {
if (document.getElementById('textarea').scrollHeight > 100) {
note.css('max-width', 300 - getScrollbarWidth());
}
var pos = getCaret(textarea[0]);
note.text(textarea.val().substring(0, pos));
$(pan).appendTo(note);
span.text(pos);
if (hidePrompt) {
hidePrompt.remove();
}
if (fakeCursor) {
fakeCursor.remove();
}
fakeCursor = $("<div style='width:5px;height:30px;background-color: #777;position: absolute;z-index:10000'> </div>");
fakeCursor.css('opacity', 0.5);
fakeCursor.css('left', $('#note span').offset().left + 'px');
fakeCursor.css('top', textarea.offset().top + note.height() - (30 + textarea.scrollTop()) + 'px');
hidePrompt = fakeCursor.clone();
hidePrompt.css({
'width': '2px',
'background-color': 'white',
'z-index': '1000',
'opacity': '1'
});
hidePrompt.appendTo(textarea.parent());
fakeCursor.appendTo(textarea.parent());
return true;
});
});
UPDATE : 나는 첫 번째 줄에는 하드 행 구분을 포함하지 만이하는 경우 잘 작동하는 것 같다 경우 오류가 있음을 볼 수 있습니다.
참고 URL : https://stackoverflow.com/questions/128342/display-div-at-cursor-position-in-textarea
'program story' 카테고리의 다른 글
YAML 스키마 유효성 검사? (0) | 2020.12.13 |
---|---|
컴파일러가 일부 명령어를 복제하는 이유는 무엇입니까? (0) | 2020.12.13 |
최고의 WPF 오픈 소스 프로젝트 (0) | 2020.12.13 |
Eclipse 3.7과 Eclipse 4.1의 차이점은 무엇입니까? (0) | 2020.12.13 |
박스가 실행되는 동안 Vagrant 관리 가상 박스의 GUI를 불러올 수 있습니까? (0) | 2020.12.13 |