variable - modificar dom javascript
Mostrar DIV en la posiciĆ³n del cursor en Textarea (11)
¿Qué tal anexar un elemento span al divisor de clonación y configurar el cursor falso en función de los desplazamientos de este tramo? He actualizado tu violín here . También aquí solo está el bit 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;
});
});
ACTUALIZACIÓN : Veo que hay un error si la primera línea no contiene saltos de línea duros, pero si lo hace, parece que funciona bien.
Esta pregunta ya tiene una respuesta aquí:
Para un proyecto mío, me encantaría proporcionar autocompletado para un área de texto específica. Similar a cómo funciona intellisense / omnicomplete. Para eso, sin embargo, tengo que averiguar la posición absoluta del cursor para saber dónde debería aparecer el DIV.
Resulta que eso es (casi espero) imposible de lograr. ¿Alguien tiene algunas ideas claras sobre cómo resolver ese problema?
Hay una descripción de un hack para la compensación de intercalación: coordenadas de intercalación Textarea X / Y - plugin jQuery
También será mejor usar el elemento div con el atributo contenteditable
si puedes usar las características html5.
No sé una solución para textarea
pero seguro que funciona para un div
con contenteditable
.
Puedes usar la API de Range
. De esta manera: (sí, realmente solo necesitas estas 3 líneas de código)
// 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());
No estoy seguro de la compatibilidad del navegador pero he encontrado que funciona en los últimos Chrome, Firefox e incluso IE7 (creo que probé 7, de lo contrario, era 9).
Incluso puedes hacer cosas ''locas'' como esta: si estás escribiendo "#hash"
y el cursor está en la última h
, puedes buscar en el rango actual para el carácter #
, mover el rango hacia atrás en n
caracteres y obtener el límite de ese rango, esto hará que el popup-div parezca ''adherirse'' a la palabra.
Un inconveniente menor es que contenteditable
puede ser un poco problemático a veces. Al cursor le gusta ir a lugares imposibles y ahora tiene que lidiar con la entrada de HTML. Pero estoy seguro de que los proveedores de navegadores abordarán estos problemas si hay más sitios que comienzan a usarlos.
Otro consejo que puedo dar es: mira la biblioteca rangy
. Intenta ser una biblioteca de rango con compatibilidad cruzada completa. No lo necesita , pero si se trata de navegadores antiguos, puede valer la pena.
Publiqué un tema relacionado con este problema en un sitio ruso de JavaScript.
Si no entiende ruso intente traducido por la versión de 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=en
Hay algunos problemas de marcado en los ejemplos de código en la versión traducida para que pueda leer el código en la publicación original de Rusia .
La idea es simple No hay un método fácil, universal y de navegador cruzado para obtener la posición del cursor en píxeles. Francamente hablando, lo hay, pero solo para Internet Explorer.
En otros navegadores, si realmente necesita calcularlo, tiene que ...
- crear un DIV invisible
- copiar todos los estilos y el contenido del cuadro de texto en ese DIV
- a continuación, inserte el elemento HTML exactamente en la misma posición en el texto donde está el símbolo de intercalación en el cuadro de texto
- obtener coordenadas de ese elemento HTML
Solucionado aquí: http://jsfiddle.net/eMwKd/4/
El único inconveniente es que la función ya proporcionada getCaret()
resuelve en la posición incorrecta al getCaret()
la tecla. por lo tanto, el cursor rojo parece estar detrás del cursor real a menos que suelte la tecla.
Voy a echarle otro vistazo.
actualización: hm, el ajuste de palabras no es preciso si las líneas son demasiado largas ...
Tenga en cuenta que esta pregunta es un duplicado de la que se hizo un mes antes, y la he respondido here . Solo mantendré la respuesta en ese enlace, ya que esta pregunta debería haberse cerrado como duplicado hace años.
Copia de la respuesta
He buscado un complemento de coordenadas textarea caret para meteor-autocomplete , por lo que he evaluado los 8 complementos en GitHub. El ganador es, por mucho, textarea-caret-position de Component .
Caracteristicas
- precisión de píxel
- ninguna dependencia en absoluto
- compatibilidad con el navegador: Chrome, Safari, Firefox (a pesar de two bugs que tiene), IE9 +; puede funcionar pero no probado en Opera, IE8 o anterior
- admite cualquier familia y tamaño de fuente, así como transformaciones de texto
- el área de texto puede tener relleno o bordes arbitrarios
- no confundido por barras de desplazamiento horizontales o verticales en el área de texto
- admite retornos duros, pestañas (excepto en IE) y espacios consecutivos en el texto
- posición correcta en líneas más largas que las columnas en el área de texto
- no hay una posición "fantasma" en el espacio vacío al final de una línea cuando se envuelven palabras largas
Aquí hay una demostración: http://jsfiddle.net/dandv/aFPA7/
Cómo funciona
Un mirror <div>
se crea fuera de la pantalla y tiene el mismo estilo que el <textarea>
. Luego, el texto del textarea hasta el símbolo de intercalación se copia en el div y se inserta un <span>
justo después. Luego, el contenido de texto del tramo se establece en el resto del texto en el área de texto, con el fin de reproducir fielmente el ajuste en el faux div.
Este es el único método garantizado para manejar todos los casos de bordes relacionados con el embalaje de líneas largas. También lo usa GitHub para determinar la posición de su menú desplegable @ usuario.
tal vez esto te guste, te indicará la posición de selección y la posición del cursor, así que intenta verificar el temporizador para obtener la posición automática o desmarca para obtener la posición haciendo clic en el botón Obtener selección
<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>
mira la demostración aquí: jsbin.com
This publicación de blog parece abordar su pregunta, pero desafortunadamente el autor admite que solo la ha probado en IE 6.
El DOM en IE no proporciona información con respecto a la posición relativa en términos de caracteres; sin embargo, proporciona valores de límite y desplazamiento para los controles renderizados por navegador. Por lo tanto, utilicé estos valores para determinar los límites relativos de un personaje. Luego, usando JavaScript TextRange, creé un mecanismo para trabajar con tales medidas para calcular la posición de Línea y Columna para fuentes de ancho fijo dentro de un Área de Texto dada.
En primer lugar, los límites relativos para TextArea se deben calcular en función del tamaño de la fuente de ancho fijo utilizada. Para hacer esto, el valor original de TextArea se debe almacenar en una variable de JavaScript local y borrar el valor. Luego, se crea un TextRange para determinar los límites superior e izquierdo de TextArea.
No volveré a explicar los problemas relacionados con este material porque están bien explicados en otras publicaciones. Solo señalará una posible solución, tiene algún error pero es un punto de partida.
Afortunadamente, hay un script en Github para calcular la posición de intercalación relativa a su contenedor, pero requiere jQuery. Página de GitHub aquí: jquery-caret-position-getter, Gracias a Bevis.Zhao.
En base a esto, he implementado el siguiente código: revíselo en acción aquí en 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>
Versión 2 de My Hacky Experiment
Esta nueva versión funciona con cualquier fuente, que se puede ajustar según demanda, y cualquier tamaño de área de texto.
Después de notar que algunos de ustedes todavía están tratando de hacer que esto funcione, decidí probar un nuevo enfoque. Mis resultados son mucho mejores esta vez, al menos en google chrome en Linux. Ya no tengo una PC con Windows disponible, así que solo puedo probar en Chrome / Firefox en Ubuntu. Mis resultados funcionan al 100% consistentemente en Chrome, digamos alrededor de 70 - 80% en Firefox, pero no creo que sea increíblemente difícil encontrar las inconsistencias.
Esta nueva versión se basa en un objeto Canvas. En mi example , realmente muestro ese mismo lienzo, solo para que pueda verlo en acción, pero podría hacerse fácilmente con un objeto de lienzo oculto.
Esto es ciertamente un truco, y me disculpo antes de tiempo por mi código bastante arrojado juntos. Como mínimo, en google chrome, funciona de forma coherente, independientemente de la fuente que establezca, o del tamaño de textarea. Usé el ejemplo de Sam Saffron para mostrar las coordenadas del cursor (un div de fondo gris). También agregué un enlace "Aleatorio", para que pueda ver que funciona en diferentes tamaños y estilos de fuente / texarea, y mire la actualización de la posición del cursor sobre la marcha. Recomiendo ver example para que pueda ver mejor el juego de lienzo acompañante.
Resumiré cómo funciona ...
La idea subyacente es que estamos tratando de volver a dibujar el área de texto en un lienzo, lo más cerca posible. Como el navegador usa el mismo motor de fuentes para ambos y texarea, podemos usar la funcionalidad de medición de fuentes de canvas para descubrir dónde están las cosas. A partir de ahí, podemos usar los métodos de lienzo disponibles para descifrar nuestras coordenadas.
En primer lugar, ajustamos nuestro lienzo para que coincida con las dimensiones del área de texto. Esto es solo para propósitos visuales ya que el tamaño del lienzo realmente no hace una diferencia en nuestro resultado. Dado que Canvas en realidad no proporciona un medio para el ajuste de palabras, tuve que conjurar (robar / tomar prestado / juntar) un medio de dividir las líneas para que coincida lo mejor posible con el área de texto. Aquí es donde probablemente encontrará que necesita hacer los ajustes más comunes entre navegadores.
Después del ajuste de palabras, todo lo demás es matemática básica. Dividimos las líneas en una matriz para imitar el ajuste de palabras, y ahora queremos pasar por esas líneas e ir hasta el punto donde termina nuestra selección actual. Para hacer eso, solo estamos contando personajes y una vez que sobrepasamos la selection.end
, sabemos que hemos bajado lo suficiente. Multiplique el recuento de líneas hasta ese punto con la altura de línea y tenga una coordenada y
.
La coordenada x
es muy similar, excepto que estamos usando context.measureText
. Mientras imprimamos el número correcto de caracteres, eso nos dará el ancho de la línea que se dibuja en el lienzo, que termina después del último carácter escrito, que es el carácter antes de la selection.end
actual. posición.
Al intentar depurar esto para otros navegadores, lo que hay que buscar es dónde las líneas no se rompen correctamente. Verá en algunos lugares que la última palabra en una línea en el lienzo puede haberse envuelto en el área de texto o viceversa. Esto tiene que ver con la forma en que el navegador maneja las palabras ajustadas. Siempre que obtenga la envoltura en el lienzo para que coincida con el área de texto, su cursor debe ser correcto.
Pegaré la fuente a continuación. Debería poder copiar y pegar, pero si lo hace, le pido que descargue su propia copia de jquery-fieldselection en lugar de presionar la de mi servidor.
También subí example y violín .
¡Buena suerte!
<!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://.com/a/11124580/14651
// https://.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>
Error
Hay un error que recuerdo. Si coloca el cursor antes de la primera letra en una línea, muestra la "posición" como la última letra de la línea anterior. Esto tiene que ver con cómo funciona la selección. No creo que sea demasiado difícil buscar ese caso y arreglarlo en consecuencia.
Versión 1
Deje esto aquí para que pueda ver el progreso sin tener que profundizar en el historial de edición.
No es perfecto y es definitivamente un truco, pero lo hice funcionar bastante bien en WinXP IE, FF, Safari, Chrome y Opera.
Por lo que puedo decir, no hay manera de averiguar directamente la x / y de un cursor en cualquier navegador. El método IE , mentioned por Adam Bellaire es interesante, pero desafortunadamente no es un navegador cruzado. Pensé que lo mejor sería usar los personajes como una grilla.
Lamentablemente, no hay información de métrica de fuente integrada en ninguno de los navegadores, lo que significa que una fuente monoespaciada es el único tipo de fuente que tendrá una medida consistente. Además, no hay medios confiables para calcular el ancho de la fuente desde la altura de la fuente. Al principio intenté usar un porcentaje de la altura, lo que funcionó muy bien. Luego cambié el tamaño de letra y todo se fue al infierno.
Intenté con un método para calcular el ancho del carácter, que era crear un área de texto temporal y seguir agregando caracteres hasta que scrollHeight (o scrollWidth) cambiaran. Parece plausible, pero a mitad de camino, me di cuenta de que podía usar el atributo cols en el área de texto y pensé que había suficientes hacks en este calvario para agregar otro. Esto significa que no puede establecer el ancho del área de texto mediante css. TIENE que usar los cols para que esto funcione.
El siguiente problema que encontré es que, incluso cuando configuras la fuente a través de css, los navegadores informan la fuente de manera diferente. Cuando no se establece una fuente, mozilla usa el mono monospace
por defecto, IE usa Courier New
, Opera "Courier New"
(con comillas), Safari, ''Lucida Grand''
(con comillas simples). Cuando establece la fuente en monospace
, mozilla y es decir, toma lo que le da, Safari sale como -webkit-monospace
y Opera se queda con "Courier New"
.
Entonces ahora inicializamos algunos vars. Asegúrese de establecer la altura de su línea en el CSS también. Firefox informa la altura de línea correcta, pero IE informaba "normal" y no me molestaba con los otros navegadores. Acabo de establecer la altura de la línea en mi CSS y eso resolvió la diferencia. No he probado con el uso de ems en lugar de píxeles. La altura de Char es solo tamaño de letra. Probablemente también deba preestablecer eso en su CSS.
Además, una configuración previa antes de comenzar a colocar personajes, lo que realmente me hizo rascar la cabeza. Para ie y mozilla, los caracteres de texarea son <cols, todo lo demás es <= caracteres. Así que Chrome puede caber 50 caracteres de ancho, pero mozilla y es decir, rompería la última palabra de la línea.
Ahora vamos a crear una matriz de posiciones de primer personaje para cada línea. Recorrimos cada char en el área de texto. Si se trata de una línea nueva, agregamos una nueva posición a nuestra matriz lineal. Si se trata de un espacio, intentamos averiguar si la "palabra" actual encajará en la línea en la que estamos o si se va a pasar a la siguiente línea. La puntuación cuenta como parte de la "palabra". No he probado con pestañas, pero hay una línea allí para agregar 4 caracteres para una pestaña de caracteres.
Una vez que tenemos una serie de posiciones de línea, hacemos un círculo y tratamos de encontrar en qué línea está el cursor. Estamos utilizando hte "Fin" de la selección como nuestro cursor.
x = (posición del cursor - primera posición del carácter de la línea del cursor) * ancho del carácter
y = ((línea de cursor + 1) * altura de línea) - posición de desplazamiento
Estoy usando jquery 1.2.6 , jquery-fieldselection y jquery-dimensions
La demo: http://enobrev.info/cursor/
Y el código:
<?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>