Window.getSelection changes after being set to another object (Reference issue?) - javascript

Hello fellow Stackers,
I seem to have a problem that my window.getselection object changes after I set it to another object. Here is an example:
document.getElementById('sendContent').focus(); //sendcontent is a contenteditable Div
current_position = window.getSelection(); //gets the selection inside sendContent
console.log(current_position); //at the console.log, the window.getSelection is the right one
pasteHtmlAtCaret(true); //here a sweetalert shows
After this, a SweetAlert pops up where the user has to enter a URL, which is then posted to the current_position .
But it seems that current_position changes in runtime, here's what happens:
At the first console.log:
current_position = Selection {anchorNode: text, anchorOffset: 27, focusNode: text,.... etc.
But after the sweetalert shows, current_position changes to:
current_position = Selection {anchorNode: div.swal2-modal.swal2-show, anchorOffset: 9, focusNode: div.swal2-modal.swal2-show,.... etc.
I tried freezing/deepfreezing current_position after I first set it but that won't work, it still changes in runtime.
It maybe has something to do with that current_position has a reference to the window.getselection property and so it changes every time the selection of the window changes. But my question is. Can I work around this?
I need the selection before the SweetAlert pops up.
EDIT 1:
This is what I want to do with the selection:
var sel, range;
if (window.getSelection) {
// IE9 and non-IE
sel = current_position;
if (sel.getRangeAt && sel.rangeCount) {
range = sel.getRangeAt(0);
range.deleteContents();
// Range.createContextualFragment() would be useful here but is
// non-standard and not supported in all browsers (IE9, for one)
var el = document.createElement("div");
el.innerHTML = html;
var frag = document.createDocumentFragment(), node, lastNode;
while ((node = el.firstChild)) {
lastNode = frag.appendChild(node);
}
range.insertNode(frag);
// Preserve the selection
if (lastNode) {
range = range.cloneRange();
range.setStartAfter(lastNode);
range.collapse(true);
sel.removeAllRanges();
sel.addRange(range);
}
}

Related

selection.toString() returns empty string despite of a selected range

I am trying to select an element's text on click using the following code:
var selection = window.getSelection();
var range = document.createRange();
range.selectNodeContents(element);
selection.removeAllRanges();
selection.addRange(range);
Although the text is getting highlighted, empty string is copied to the clipboard on using Ctrl + C. Checking for selection.toString() returns an empty string too. Any idea why can this be happening?
Hm, I took a look at your code and tried:
var selection = window.getSelection();
var selectionText = selection.anchorNode.textContent
and I got the selected text content.
EDIT: it appears this was wrapped in a click function...one second.
$('<your selector goes here>').click(function(e) {
var selection = window.getSelection();
var range = document.createRange();
range.selectNodeContents(e.target);
selection.removeAllRanges();
selection.addRange(range);
console.dir(selection.anchorNode.textContent);
//text content should display...
//now that the content is highlighted, you can copy it
document.execCommand('copy');
})

js/jquery: contenteditable, insert text and move cursor to end

I need to insert text into a contenteditable div and then have the cursor be at the end of the inserted text.
I got the solution below from here Insert text at cursor in a content editable div.
That works great if the text is added to am empty div. But it does not work if the user has already typed in text. Or, if the function is used to insert text, the user then places the cursor somewhere inside the newly inside text, and the function is then called again. Then the cursor is left at the beginning of the inserted text.
EDIT: The code below works in IE, properly setting the cursor, but has the problem in Chrome.
function insertTextAtCursor(text) {
var sel, range, html;
if (window.getSelection) {
sel = window.getSelection();
if (sel.getRangeAt && sel.rangeCount) {
range = sel.getRangeAt(0);
range.deleteContents();
range.insertNode( document.createTextNode(text) );
}
} else if (document.selection && document.selection.createRange) {
document.selection.createRange().text = text;
}
}
Alright, once I realized it was a Chrome/IE thing I managed to find the answer tucked away inside one of the comments to the answer that I first found.
function insertTextAtCursor(text) {
var sel, range, html;
sel = window.getSelection();
range = sel.getRangeAt(0);
range.deleteContents();
var textNode = document.createTextNode(text);
range.insertNode(textNode);
range.setStartAfter(textNode);
sel.removeAllRanges();
sel.addRange(range);
}

Set anchor name with execCommand

I know how to set an <a /> tag with the href attribute in a contenteditable like this:
execCommand("CreateLink", false, "#jumpmark");
which will result in
selection
However I cannot figure out how to set an anchor name instead of the href.
This is my desired result:
<a name="jumpmark">selection</a>
Can anyone help me?
Side notes: I am using jQuery and Rangy as libraries, however I would prefer a solution that works directly with execCommand.
Update: Here's a jsfiddle: http://jsfiddle.net/fjYHr/ Select some text and click the button. All I want is that with the button click a link is inserted with a name attribute set instead of the href.
You could use something like the following, which is adapted from the pasteHtmlAtCaret() function from this answer of mine:
Demo: http://jsfiddle.net/F8Zny/
Code:
function surroundSelectedText(element) {
var sel, range;
if (window.getSelection) {
// IE9 and non-IE
sel = window.getSelection();
if (sel.getRangeAt && sel.rangeCount) {
range = sel.getRangeAt(0);
element.appendChild( document.createTextNode(range.toString()) );
range.deleteContents();
range.insertNode(element);
// Preserve the selection
range = range.cloneRange();
range.setStartAfter(element);
range.collapse(true);
sel.removeAllRanges();
sel.addRange(range);
}
} else if (document.selection && document.selection.type != "Control") {
// IE < 9
var selRange = document.selection.createRange();
element.appendChild( document.createTextNode(selRange.text) );
selRange.pasteHTML(element.outerHTML);
}
}
If you must use document.execCommand() then you could use the InsertHTML command in non-IE browsers. However, IE does not support it.
document.execCommand("InsertHTML", false, '<a name="jumpmark">selection</a>');
I see you're using Rangy, but I don't how to use it at all. Before I realized what Rangy was, I looked up how to get the current selection. I found a function that gets it and replaces it with a passed in value. I ended up modfiying it, but here it is:
http://jsfiddle.net/fjYHr/1/
$(document).ready(function () {
$("#setlink").click(function () {
replaceSelectedText("jumplink");
});
});
function replaceSelectedText(nameValue) {
var sel, sel2, range;
if (window.getSelection) {
sel = window.getSelection();
sel2 = ""+sel; // Copy selection value
if (sel.rangeCount) {
range = sel.getRangeAt(0);
range.deleteContents();
var newA = document.createElement("a");
newA.name = nameValue;
newA.innerHTML = sel2;
range.insertNode(newA);
}
} else if (document.selection && document.selection.createRange) {
// Not sure what to do here
range = document.selection.createRange();
var newA = "<a name='" + nameValue.replace(/'/g, "") + "'>" + range.text + "</a>";
range.text = newA;
}
}
Notice how I store the original current selection, then replace it with an <a> element that gets its name set with the passed-in value.
As for the document.selection part (which seems to be used by IE < 9), I'm not 100% sure that the code I provided will work (actually allow HTML in the selection, and not escaping it). But it's my attempt :)
As you've seen execCommand is rather limited in the attributes you can set, as such you cannot set the name attribute using it - only the href.
As you have jQuery set as a tag, you can use that as an alternative:
var $a = $('<a></a>').attr('name', 'jumpmark').appendTo('body');
Update
I need to work on the current selection. Specifically I don't have a jQuery object that I can append to, meaning I don't have a DOM node that I can work on
In this case use a plugin such as Rangy to get the selection which you can then amend with jQuery as required.

javascript replace selection all browsers

Is there a simple js function I can use to replace the current document's selection with some html of mine?
For instance say the document contains a <p>AHAHAHA</p> somewhere and user selects the 1st "ha" text chunk.
Now I want to replace this with something like: <span><font color="red">hoho</font></span>
When I google for *javascript replace selection * I can't get a simple straightforward answer!
Yes. The following will do it in all major browsers, with an option to select the inserted content afterwards as requested in the comments (although this part is not implemented for IE <= 8):
Live demo: http://jsfiddle.net/bXsWQ/147/
Code:
function replaceSelection(html, selectInserted) {
var sel, range, fragment;
if (typeof window.getSelection != "undefined") {
// IE 9 and other non-IE browsers
sel = window.getSelection();
// Test that the Selection object contains at least one Range
if (sel.getRangeAt && sel.rangeCount) {
// Get the first Range (only Firefox supports more than one)
range = window.getSelection().getRangeAt(0);
range.deleteContents();
// Create a DocumentFragment to insert and populate it with HTML
// Need to test for the existence of range.createContextualFragment
// because it's non-standard and IE 9 does not support it
if (range.createContextualFragment) {
fragment = range.createContextualFragment(html);
} else {
// In IE 9 we need to use innerHTML of a temporary element
var div = document.createElement("div"), child;
div.innerHTML = html;
fragment = document.createDocumentFragment();
while ( (child = div.firstChild) ) {
fragment.appendChild(child);
}
}
var firstInsertedNode = fragment.firstChild;
var lastInsertedNode = fragment.lastChild;
range.insertNode(fragment);
if (selectInserted) {
if (firstInsertedNode) {
range.setStartBefore(firstInsertedNode);
range.setEndAfter(lastInsertedNode);
}
sel.removeAllRanges();
sel.addRange(range);
}
}
} else if (document.selection && document.selection.type != "Control") {
// IE 8 and below
range = document.selection.createRange();
range.pasteHTML(html);
}
}
Example:
replaceSelection('<span><font color="red">hoho</font></span>', true);
You can use the Rangy library
http://code.google.com/p/rangy/
You can then do
var sel = rangy.getSelection();
var range = sel.getRangeAt(0);
range.deleteContents();
var node = range.createContextualFragment('<span><font color="red">hoho</font></span>');
range.insertNode(node);

how to modify the document selection in javascript?

I wanna modify the document selection (user currently selected by mouse or keyboard), how to do it in a cross browser way?
I have not worked with text selection enough to provide real help, but what you are trying to do can be done. You will want to look into the following two functions:
createRange() MSDN | MDC
getRangeAt() MDC
I know it can be implemented cross browser. You can see some of it in action here:
http://fuelyourcoding.com/a-few-strategies-for-using-javascript/
By scrolling to the bottom and clicking the Elephant Icon, which uses the Evernote script. However, my script first selects the main content area (you will see it flash orange) and then it deselects once the capture is made.
Here is a mini jQuery plugin that does it. It was adapted by me from some site, and like the comments say, I feel horrible for not remembering. Its really important to note I adapted it to jQuery, but the code came from some site where they explained how to do it:
// Adapted this from somewhere. Feel horrible for not remembering.
$.fn.autoSelect = function(){
var selectTarget = this[0]; // Select first element from jQuery collection
if(selectTarget != null) {
if(selectTarget.tagName == 'TEXTAREA' || (selectTarget.tagName == "INPUT" && selectTarget.type == "text")) {
selectTarget.select();
} else if(window.getSelection) { // FF, Safari, Opera
var sel = window.getSelection();
var range = document.createRange();
range.selectNode(selectTarget);
sel.removeAllRanges();
sel.addRange(range);
} else { // IE
document.selection.empty();
var range = document.body.createTextRange();
range.moveToElementText(selectTarget);
range.select();
};
};
return this; // Don't break the chain
};
It seems this script is a few places online, but here is another variation on it
As an example, and the easiest one, let's say you want to move the user's selection to contain the contents of an element. The following will work in all major browsers:
function selectElementContents(el) {
var body = document.body, range, sel;
if (body.createTextRange) {
range = body.createTextRange();
range.moveToElementText(el);
range.select();
} else if (document.createRange && window.getSelection) {
range = document.createRange();
range.selectNodeContents(el);
sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);
}
}
selectElementContents( document.getElementById("someElement") );

Categories