Display DIV at cursor position in Textarea

For my project, I would like to provide automatic completion for a specific text field. Similar to how intellisense / omnicomplete works. For this, however, I have to find the absolute cursor position so that I know where the DIV should look.

It turns out that this (I hope) is impossible to achieve. Does anyone have any neat ideas on how to solve this problem?

+58
javascript dom
Sep 24 '08 at 16:55
source share
11 answers

Version 2 of my Hacky Experiment

This new version works with any font that can be customized on demand, and any size of the text field.

Noticing that some of you are still trying to get this to work, I decided to try a new approach. This time my FAR results are better - at least on google chrome on linux. I no longer have a Windows computer, so I can only check on chrome / firefox on Ubuntu. My results work 100% sequentially in Chrome, and let's say somewhere around 70 - 80% on Firefox, but I don’t think it would be incredibly difficult to find inconsistencies.

This new version is based on the Canvas object. In my example , I actually show this very canvas - just so you can see it in action, but it could be easily done with a hidden canvas.

This is definitely a hack, and I apologize in advance for my rather complicated code. At least in google chrome it works sequentially, no matter what font I set for it, or the size of the text field. I used the example of Sam Shaffron to show the coordinates of the cursor (gray-background div). I also added a “Randomize” link so you can see how it works in different font / texarea sizes and styles and keeps an eye on the cursor position updates on the fly. I recommend watching a full-screen demo so you can better see the canvas canvas.

I'll tell you how it works ...

The main idea is that we try to redraw the text area on the canvas as much as possible. Since the browser uses the same font engine for both sides and the texaria, we can use the canvas font metering function to find out where this happens. From there, we can use the available canvas methods to find out our coordinates.

First of all, we adjust our canvas according to the size of the text field. This is entirely for visual purposes, since the size of the canvas does not really affect our result. Since Canvas doesn’t actually provide a means of wrapping words, I had to conjure (steal / borrow / merge together) to break the lines into the greatest possible match of the text area. This is where you will probably find that you need to do the most cross-browser setup.

After word wrapping, everything else is basic math. We split the lines into an array to simulate word wrap, and now we want to scroll through these lines and get to the end of our current selection. To do this, we simply count the characters, and as soon as we surpass selection.end , we know that we have come down far enough. Multiply the line count to this point with the line height, and you have the y coordinate.

The x coordinate is very similar, except that we use context.measureText . While we print the desired number of characters, this will give us the width of the line that stretches to the Canvas, which ends after the last character drawn, which is the character before the currentl selection.end position.

When trying to debug this for other browsers, you need to look for where the lines will not break properly. In some places, you will see that the last word in a line in a canvas can be wrapped in a text box or vice versa. This is due to the way the browser handles word wraps. As long as you get the wrapping in the canvas according to the text box, your cursor should be correct.

I will insert the source below. You should be able to copy and paste it, but if you do, I ask you to upload your own copy of jquery-fieldselection instead of clicking on the one on my server.

I also picked up a new demo as well as a violin .

Good luck

 <!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> 

Mistake

There is one mistake that I remember. If you place the cursor in front of the first letter in a line, it will display the "position" as the last letter on the previous line. This is due to how the selection.end function works. I do not think that it should be too difficult to find for this case and correct it accordingly.




Version 1

Leaving this here so you can see progress without having to scroll through the change history.

This is not ideal, and it most definitely collapses, but I got it to work very well in WinXP IE, FF, Safari, Chrome and Opera.

As far as I can tell, there is no way to directly recognize the x / y cursor in any browser. The IE method mentioned by Adam Bellaire is interesting, but unfortunately not a cross browser. I thought the best thing is to use symbols in the form of a grid.

Unfortunately, there is no font metric information built into any of the browsers, which means that a monospaced font is the only type of font that will have a consistent dimension. In addition, there is no reliable means of determining the font width from the font height. At first I tried using a percentage of height that worked great. Then I changed the font size and it all went to hell.

I tried one method for determining the width of characters, which was to create a temporary text area and continue to add characters until scrollHeight (or scrollWidth) changes. This seems plausible, but about halfway down this road, I realized that I could just use the cols attribute on the text box, and thought there were enough hacks in this test to add another one. This means that you cannot set the width of the text area via css. You must use cols for this.

The next problem I came across is that even when you install the font through css, browsers report the font differently. When you do not install the font, mozilla uses monospace by default, IE uses Courier New , Opera "Courier New" (with quotes), Safari, 'Lucida Grand' (with single quotes). When you install the font on monospace , mozilla, etc. Take what you give them, Safari comes out as -webkit-monospace , and Opera stays with "Courier New" .

So now we initialize some vars. Be sure to set the line height in css. Firefox reports the correct line height, but IE reported "normal" and I did not worry about other browsers. I just set the line height in my css and solved the difference. I have not tested using ems instead of pixels. Char height - only font size. Probably pre-install this in your CSS.

In addition, another pre-setting before we begin to post characters, which really made me scratch my head. For that is mozilla, texarea characters <cols, everything else is <= chars. Thus, Chrome can fit 50 characters across, but mozilla, etc. Breaks the last word from the line.

Now we will create an array of positions of the first character for each line. We look at each Char in the text box. If this is a new line, we add a new position to our line array. If this is a space, we try to find out whether the current "word" will fit into the line on which we are, or if it will be transferred to the next line. Punctuation is considered part of the "word". I have not tested with tabs, but there is a line there to add 4 characters for the char tab.

As soon as we have an array of line positions, we scroll and try to find which line the cursor is on. We use the hte "End" of choice as our cursor.

x = (cursor position - position of the first character of the cursor line) * character width

y = ((cursor line + 1) * line height) - scroll position

I am using jquery 1.2.6 , jquery- fieldselection and jquery-dimensions

Demo: http://enobrev.info/cursor/

And the code:

 <?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> 
+35
02 Oct '08 at 16:47
source share

I posted a topic related to this issue on the Russian JavaScript site.

If you do not understand that the Russian attempt is translated according to Google: http://translate.google.com/translate?js=y&prev=_t&hl=en&ie=UTF-8&layout=1&eotf=1&u=http://javascript.ru/forum /events/7771-poluchit-koordinaty-kursora-v-tekstovom-pole-v-pikselyakh.html&sl=en&tl=en

Thre - these are some problems with the markup in the code examples in the translated version, so you can read the code in the original message in Russia .

The idea is simple. There is no simple, universal, or cross-browser method for getting the cursor position in pixels. Honestly, there is, but only for Internet Explorer.

In other browsers, if you really need to calculate it, you need ...

  • create an invisible div
  • copy all styles and text box contents to DIV
  • then paste the HTML element at exactly the same position in the text where the caret is in the text box
  • get the coordinates of this HTML element
+4
Feb 18 '10 at 17:26
source share

I will no longer explain the problems associated with this material because they are well explained in other posts. Just point out a possible solution, it has an error, but this is the starting point.

Fortunately, Github has a script for calculating the caret position relative to the container, but this requires jQuery. The GitHub page is here: jquery-caret-position-getter, Thanxs for Bevis.Zhao.

Based on this, I have implemented the following code: check it in action here in 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> 
+4
Oct 26 '12 at 8:23
source share

Please note that this question is a duplicate of the question asked a month earlier, and I answered it here . I will only support the answer on this link, since this question should have been closed as repeating several years ago.

Copy of answer

I was looking for a textarea caret coordinate plugin for meteor-autocomplete , so I rated all 8 plugins on GitHub. The winner is certainly textarea-caret-position from Component .

Functions

  • pixel accuracy
  • no dependencies
  • browser compatibility: Chrome, Safari, Firefox (despite two errors), IE9 +; may work, but not tested in Opera, IE8 or later
  • supports any family and font size, as well as text conversions
  • text area may have arbitrary padding or borders
  • do not confuse horizontal or vertical scroll bars in the text box
  • supports hard returns, tabs (except IE) and consecutive spaces in the text
  • correct position in rows longer than columns in text area
  • no ghost position in empty space at the end of the line when wrapping long words

Here's the demo - http://jsfiddle.net/dandv/aFPA7/

enter image description here

How it works

The <div> mirror is created off-screen and is created in exactly the same way as <textarea> . Then the text of the text area to the caret is copied to the div and a <span> inserted immediately after it. Then the text content of the range is set to the rest of the text in the text box to accurately reproduce the wrapper in the faux div.

This is the only method that is guaranteed to handle all boundary cases associated with wrapping long lines. It is also used by GitHub to locate its @ drop-down list.

+2
Mar 17 '14 at 12:50
source share

This blog seems to answer the question too closely. I have not tried this myself, but the author says that he was tested using FF3, Chrome, IE, Opera, Safari. The code is on github

+1
Oct 24
source share

fixed it here: http://jsfiddle.net/eMwKd/4/

the only drawback is that the already provided getCaret() function resolves the wrong position when a key is pressed. therefore, the red cursor seems to be behind the real cursor if you do not release the key.

I will have another look at him.

update: hm, word-wrapping is inaccurate if the lines are too long ..

+1
Oct 27
source share

This blog post seems to be about your question, but unfortunately the author admits that he tested it only in IE 6.

The DOM in IE does not provide information regarding the relative position in terms of characters; however, it does provide bounding and biased values ​​for the controls displayed in the browser. Thus, I used these values ​​to determine the relative boundaries of the character. Then, using JavaScript TextRange, I created a mechanism to work with such measures to calculate the position of Line and Column for fixed-width fonts in a given TextArea.

First, relative estimates for TextArea must be calculated based on a fixed-width font size. To do this, the original TextArea value must be stored in a local JavaScript variable and clear the value. Then a TextRange is created to define the top and left borders of the TextArea.

0
Sep 24 '08 at 17:18
source share

I do not know the solution for textarea , but it works for a div with contenteditable .

You can use the Range API. For example: (yes, you really only need these 3 lines of code)

 // get active selection var selection = window.getSelection(); // get the range (you might want to check selection.rangeCount // to see if it popuplated) var range = selection.getRangeAt(0); // will give you top, left, width, height console.log(range.getBoundingClientRect()); 

I'm not sure about browser compatibility, but found that it works in the latest Chrome, Firefox, and even IE7 browsers (I think I tested 7, otherwise it was 9).

"" : "#hash" , h , # , n , popup-div, , "" .

, contenteditable . , HTML. , , .

, : rangy . - . , , .

0
26 . '12 0:06
source share

, , , , , , " "

  <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

0
28 . '12 18:13
source share

: Textarea X/Y - jQuery

div contenteditable , html5.

0
30 . '12 19:14
source share

span div ? . 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'>&nbsp;</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 . , , , , .

-one
31 . '12 5:11
source share



All Articles