I made the following function to insert a message (msg) into a textfield.
After inserting the text the cursor needs to be after the last character of the msg that was inputted. The textfield already contains some text.
When I insert the message the cursor gets focussed somewhere near the end of the msg but certainly not after the last character. It seems like some characters don't get counted by .length?
function insertAtCursor(msg) {
var textArea = document.getElementsByName("message")[0];
textArea.value = textArea.value.substr(0, textArea.selectionStart) + msg + textArea.value.substr(textArea.selectionEnd);
var endMsgPos = textArea.value.lastIndexOf(msg) + msg.length;
textArea.setSelectionRange(endMsgPos, endMsgPos);
}
There's no need for the textArea.value.lastIndexOf(msg).
function insertAtCursor(msg) {
var textArea = document.getElementsByName("message")[0];
var selStart = textArea.selectionStart, val = textArea.value;
textArea.value = val.slice(0, selStart) + msg +
val.slice(textArea.selectionEnd);
var endMsgPos = selStart + msg.length;
textArea.setSelectionRange(endMsgPos, endMsgPos);
}
First of all, you'll have an issue with IE 8 if you try to use textArea.selectionStart because it uses a different API.
Secondly lastIndexOf will behave strange in your code if the caret is at the begging of the string and somewhere at the end the value of the msg is present.
Try this:
function insertAtCursor(msg) {
var e = document.getElementsByName('message')[0],
length = msg.length, val = e.value;
/* mozilla / dom 3.0 */
if ('selectionStart' in e){
var partial = val.slice(0, e.selectionStart) + msg;
e.value = partial + val.slice(e.selectionEnd);
var end = partial.length;
e.setSelectionRange(end, end);
}
/* exploder */
if (document.selection){
e.focus();
document.selection.createRange().text = msg;
}
}
The demo is here : http://jsbin.com/UnOBUbU/6
Related
I have an input box on a html page. I know I can get just the value, but I want the entire input string, i.e. , but with the value present:
<input id="myInput" value="my entry that I just typed in"/>
I have tried innerHTML, I have tried XMLSerializer, etc.
var htmlDiv = document.getElementById('myInput');
var str = s.serializeToString(htmlDiv);
The value is always empty.
If you are wondering why I want to do this - it is because this is my simple example of what in reality is about 60 inputs, all part of an HTML string that I want to send to XSLT translation. That part works like gangbusters, I just have to get it HTML with values intact.
Another related problem is that innerHTML has a nasty habit of getting rid of the / at the end of the input box, which throws off the XSLT translation.
Try this:
console.log(document.getElementById("myInput").outerHTML);
<input id="myInput" value="my entry that I just typed in"/>
And if you want to add / at the end:
myVar = document.getElementById("myInput").outerHTML;
if(myVar.charAt(myVar.length - 1) !== "/"){
console.log(myVar.slice(0, myVar.length-1) + "/>");
}
<input id="myInput" value="my entry that I just typed in"/>
I ended up doing the following: Serializing with XMLSerializer, which solved the / problem. And I just got the values from the DOM and inserted them myself.
function htmlCleanup(htmlDiv) {
//serialize the html. It will lack the latest user changes in the inputs.
var s = new XMLSerializer();
var str = s.serializeToString(htmlDiv);
var lines = str.split("\n");
//get all of the inputs in the div
var inputs = htmlDiv.getElementsByTagName('input');
//Here we put in the latest values
var inputIndex = 0;
for (var i = 0; i < lines.length; i++) {
var line = lines[i].trim();
if (line.indexOf('<input') >= 0) {
var value = inputs[inputIndex].value;
lines[i] = fixInputValue(line, value);
inputIndex++;
}
}
str = lines.join('\n');
//Some other weird aftertaste stuff that needs fixing. <tbody> is added to tables - wrongly.
//the div at the top is also a problem.
//Then we turn it all into proper xhtml for the trip back to the server.
var contents = str.replace('<div xmlns="http://www.w3.org/1999/xhtml" id="documentEditBody">', '');
contents = contents.replace(/<tbody>/g, '');
contents = contents.replace(/<\/tbody>/g, '');
contents = '<?xml version="1.0" encoding="UTF-8"?><html><head></head><body><div>' + contents + '</body></html>';
return contents;
}
function fixInputValue(input, value) {
var valuePos = input.indexOf('value');
var result = "";
if (valuePos > -1) {
for (var i = valuePos + 7; i < input.length; i++) {
var chr = input[i];
if (chr === '"') {
var last = input.substring(i + 1, input.length)
result = input.substring(0, valuePos - 1) + ' value="' + value + '" ' + last;
break;
}
}
}
return result;
}
I just want to remember always the last position in a textarea, but I fail always.
I've got this code:
//This function catch the position
(function ($, undefined) {
$.fn.getCursorPosition = function() {
var el = $(this).get(0);
var pos = 0;
if('selectionStart' in el) {
pos = el.selectionStart;
} else if('selection' in document) {
el.focus();
var Sel = document.selection.createRange();
var SelLength = document.selection.createRange().text.length;
Sel.moveStart('character', -el.value.length);
pos = Sel.text.length - SelLength;
}
return pos;
}
})(jQuery);
//Insert the text by clicking an icon
function insertar(texto) {
var div = $('textarea');
var text = div.val();
var pos = div.getCursorPosition();
text = text.substring(0,pos) + " " + texto + text.substring(pos);
div.val(text);
}
$('#icon').click(function(evt){ insertar(':icon:'); });
Well, if I write this:
"hello hello"
And I want to add this with the function:
"hi hi hello hello"
It give me this:
"hi hello hello hi."
I think the second time I execute the function, this forget the position and add the text at the end of the text.
PD: I'm adding text clicking on an image. I'm trying to do a quick reply fieldset for a forum.
Anyone knows where the problem is?
Here is my demo where you can see it: http://jsfiddle.net/ko295zpw/6/
One option would be to set a bool that determines whether or not you need to re-find the cursor position, then reset the bool to true every time the textarea is clicked and set it to false after you've found the position the first time after the user has clicked on the textarea. You'd need to move the declarations of both var pos and the bool to a scope outside the insertar function. So:
var encontrarPos = true;
var pos = 0;
function insertar(texto) {
var div = $('textarea');
var text = div.val();
if (encontrarPos) pos = div.getCursorPosition();
encontrarPos = false;
console.log(pos);
text = text.substring(0,pos) + texto + " " + text.substring(pos);
div.val(text);
}
$('textarea').click(function() {
encontrarPos = true;
});
And here's the updated Fiddle
I'm having a problem with preserving tab characters when reading the value of a textarea.
I'm adding the tabs as follows:
$("#code-editor").keydown(function (e) {
if (e.keyCode === 9) { // tab was pressed
// get caret position/selection
var start = this.selectionStart;
var end = this.selectionEnd;
var $this = $(this);
var value = $this.val();
// set textarea value to: text before caret + tab + text after caret
$this.val(value.substring(0, start)
+ "\t"
+ value.substring(end));
// put caret at right position again (add one for the tab)
this.selectionStart = this.selectionEnd = start + 1;
// prevent the focus lose
e.preventDefault();
}
});
The tabs are inserted correctly and are displayed in the textarea as expected.
The problem occurs when I then read the value of the textrea and try to replace to tab characters with html formatting.
$("#code-editor").keyup(function (e) {
var value = $(this).val();
//Do formatting
var lines = value.split("\n");
var newvalue = "";
for (var i = 0; i < lines.length; i++) {
lines[i].replace(/\t/g, "<span style='padding-left:3em'></span>");
lines[i] += "<br />";
newvalue += lines[i];
}
$('#editor-displayarea').html(newvalue);
});
I have discovered that the tabs do not seem to be preserved when reading from textarea.
Is there a way around this or have I taken the wrong approach?
Thanks.
Update:
I have tried using a few variations on the regular expression as stated in the comment below but to no avail.
You are not storing the value when you replace the tabs. Try something like this (I would leave the in the span as well):
for (var i = 0; i < lines.length; i++) {
var replaced = lines[i].replace(/\t/g, "<span style='padding-left:3em'> </span>");
replaced += "<br />";
newvalue += replaced;
}
I need to get the user selected area of a textarea and then insert <a> tags round it.
I use this to get the user selected area:
var textComponent = document.getElementById('article');
var selectedText;
if (document.selection != undefined)
{
textComponent.focus();
var sel = document.selection.createRange();
selectedText = sel.text;
}
// Mozilla version
else if (textComponent.selectionStart != undefined)
{
var startPos = textComponent.selectionStart;
var endPos = textComponent.selectionEnd;
selectedText = textComponent.value.substring(startPos, endPos)
}
Now, I know I can do a string search for the user selected text and insert a tags round it, but what happens if that user selected text appears twice in the text, for example.
Hello to you, goodbye to you.
If the user highlights the second 'you' for the link they want, surely a string replace would put a tags around every instance of 'you'.
Whats the best way to do this?
You could use my jQuery plug-in for this (demo):
$("#article").surroundSelectedText('', "");
Alternatively you could use the getInputSelection() function that I've posted on Stack Overflow a few times to get the selection start and end character indices in all major browsers and then do string substitution on the textarea's value:
var sel = getInputSelection(textComponent);
var val = textComponent.value;
textComponent.value = val.slice(0, sel.start) +
'<a href="foo.html">' +
val.slice(sel.start, sel.end) +
"</a>" +
val.slice(sel.end);
Why capture the selected text at all? What you really want is the start/end positions to drop in tags.
var textComponent = document.getElementById('article');
var selectedText;
var startPos;
var endPos;
// the easy way
if (textComponent.selectionStart != undefined)
{
startPos = textComponent.selectionStart;
endPos = textComponent.selectionEnd;
}
// the hard way
else if (document.selection != undefined)
{
textComponent.focus();
var sel = document.selection.createRange();
var range = document.selection.createRange();
var stored_range = range.duplicate();
stored_range.moveToElementText(textComponent);
stored_range.setEndPoint( 'EndToEnd', range );
startPos = stored_range.text.length - range.text.length;
endPos = startPos + range.text.length;
}
// add in tags at startPos and endPos
var val = textComponent.value;
textComponent.value = val.substring(0, startPos) + "<a>" + val.substring(startPos, endPos) + "</a>" + val.substring(endPos);
IE code modified from this reference.
EDIT: Note Tim Down's comment about newlines. Also, probably use his soltion, because it's better.
UPDATE: apparently, the issue I've been experiencing only affects the first element in the selection.
JSFiddle: http://jsfiddle.net/NhQCR/
My code that does the selecting from the web page:
if (!window.TB) {
TB = {};
}
TB.Selector = {};
// http://stackoverflow.com/questions/5643635/how-to-get-selected-html-text-with-javascript
// and
// http://stackoverflow.com/questions/7690319/javascript-how-do-i-expand-a-user-selection-based-on-html-tags
TB.Selector.getSelected = function() {
var html = "";
if (typeof window.getSelection != "undefined") {
var sel = window.getSelection();
if (sel.rangeCount) {
var range = sel.getRangeAt(0);
var startEl = sel.anchorNode;
if (startEl != range.commonAncestorContainer) {
while (startEl.parentNode != range.commonAncestorContainer) {
startEl = startEl.parentNode;
}
}
var endEl = sel.focusNode;
if (endEl != range.commonAncestorContainer) {
while (endEl.parentNode != range.commonAncestorContainer) {
endEl = endEl.parentNode;
}
}
range.setStartBefore(startEl);
range.setEndAfter(endEl);
sel.addRange(range);
var container = document.createElement("div");
container.appendChild(sel.getRangeAt(0).cloneContents());
html = container.innerHTML;
}
} else if (typeof document.selection != "undefined") {
if (document.selection.type == "Text") {
html = document.selection.createRange().htmlText;
}
}
return html;
}
and how I get the selected text:
var selected_text = TB.Selector.getSelected();
unfortunately, as seen in the picture, this method for selecting text remove certain white space characters, and adds an empty div at the end of the selection
This is what the selection looks like on the webpage:
For those wondering: this is how I got the alert to show what it does:
start_index = $j(".text_container").html().indexOf($selected_text);
end_index = start_index + $selected_text.length;
alert(start_index + " to " + end_index + "\n" + $selected_text + "\n=====================================\n" + $j(".text_container").html());
Currently, the selection code will expand the selection to make sure there are no unmatched tags.
I want code that will get me exactly what is on the webpage, without removing / adding anything =\ I just don't know where to start =\
I can't reproduce the first issue, "empty space before 9", even when i use It might be due to charsets, or little greens guys too. Anyway, all my selections starts at the correct point.
For the second issue the <div></div> at the end, simply replace
range.setEndAfter(endEl);
by
range.setEndBefore(endEl);
This sounds sound as you select stuff in an array from 0 to length-1.