Simulate cut function with JavaScript? - javascript

I'm working on a chrome extension and I want to do something with the text that is selected (highlighted by the user) on the page. For that, I need a way to remove the selected text, for example text inside an input field.
I found a way to "clear" the selected text, meaning it will be unselected:
Clear Text Selection with JavaScript But it doesn't seem to be what I'm looking for.
This just removes the highlighting from the text:
window.getSelection().empty();
I want to remove the text that is selected, if it's editable text. Is this possible with JavaScript?

You can use the deleteFromDocument method:
window.getSelection().deleteFromDocument()
This will immediately remove the selected content from the document, and as such also clear the selection.
As described formally in the MDN web docs:
The deleteFromDocument() method of the Selection interface deletes the selected text from the document's DOM.
If you'd like to be able to delete text from input elements instead, you need to use different APIs:
var activeEl = document.activeElement;
var text = activeEl.value;
activeEl.value = text.slice(0, activeEl.selectionStart) + text.slice(activeEl.selectionEnd);
Edit from me, Synn Ko: to cover input fields, textareas and contenteditables, use this:
var selection = window.getSelection();
var actElem = document.activeElement;
var actTagName = actElem.tagName;
if(actTagName == "DIV") {
var isContentEditable = actElem.getAttribute("contenteditable"); // true or false
if(isContentEditable) {
selection.deleteFromDocument();
}
}
if (actTagName == "INPUT" || actTagName == "TEXTAREA") {
var actText = actElem.value;
actElem.value = actText.slice(0, actElem.selectionStart) + actText.slice(actElem.selectionEnd);
}

Related

How do I get all the HTML selection tags in selected text using Javascript?

I have a minimal Rich text editor created using Javascript and Swift(for use in iOS), that supports simple text formatting like bold, italics and underline using the execCommand command(the formatting actions like bold are outside of the web view in a SwiftUI view). How can I get ALL the tags that encloses the selected text by executing a javascript query? Is there any simple way to do this?
For example, this is slack and when I select the text in the first image it highlights the bold, but when I place the cursor on the "u" in jumps, bold and italics are highlighted.
Check out this solution, based on Selection API
(press Run Code Snippet below to see how it works)
const text = document.querySelector('#text');
const textElements = document.querySelectorAll('#text *');
document.addEventListener('selectionchange', (e) => {
const selection = window.getSelection();
if (selection && selection.anchorNode && selection.focusNode) {
const selectionStartsAtElement = selection.anchorNode.parentNode;
const selectionEndsAtElement = selection.focusNode.parentNode;
if (selectionStartsAtElement === text && selectionEndsAtElement === text) {
console.log('no markup elements have been selected');
return;
}
const areElementsSelectedInsideText = text.contains(selectionStartsAtElement) && text.contains(selectionEndsAtElement);
if (areElementsSelectedInsideText) {
console.log('markup elements have been selected');
let parentElementsForSelectionEnd;
let parentElementsForSelectionStart;
// get selection end elements
if (selectionEndsAtElement !== text) {
parentElementsForSelectionEnd = [selectionEndsAtElement];
let nextParentAtSelectionEnd = selectionEndsAtElement.parentNode;
while (nextParentAtSelectionEnd !== text) {
parentElementsForSelectionEnd.push(nextParentAtSelectionEnd);
nextParentAtSelectionEnd = nextParentAtSelectionEnd.parentNode;
}
}
// get selection start elements
if (selectionStartsAtElement !== text) {
parentElementsForSelectionStart = [selectionStartsAtElement];
let nextParentAtSelectionStart = selectionStartsAtElement.parentNode;
while (nextParentAtSelectionStart !== text) {
parentElementsForSelectionStart.push(nextParentAtSelectionStart);
nextParentAtSelectionStart = nextParentAtSelectionStart.parentNode;
}
}
// do what ever you need here
// (as I understood - you should highlight buttons somewhere)
console.log(parentElementsForSelectionStart);
console.log(parentElementsForSelectionEnd);
}
}
});
<div id='text'>Test with <i>tags <b>and formatting</b></i></div>
Note! This code is not final and is not production ready.
I'm sure that there are a lot of edge cases and additional requirements to it + it should be refactored (I have created such an example just to make it clear)

JavaScript selected text entirely within node

I'm looking into text selection and ranges in JavaScript.
I need to get any nodes that surround the selected text exactly, for example:
<div>this is <span>some simple</span> text</div>
When the user selects the words 'some simple' i need to know that it sits entirely within the node .
Yet if they select just 'some' then this is not entirely within the node as the word 'simple' is NOT selected.
The end requirement is to be able to amend the class on the node only if the whole text within the node is selected.
jquery is also viable. thanks
To add some more context to this, when a user selects some text we add some sytling to it, let's say 'bold'. the user can edit the text in the parent div as often as they wish so each edit could add a new span enclosing the selected text. We could end up with something like this:
<div><span class="text-bold">Hi</span>, <span class="text-red">this <span class="text-italic">is</span></span> a sample text item</div>
So the spans can come and go dependant on what the user wants.
You are looking to get the DOM each time. So you can set id to your HTML Element Objects and get the value from them. For example:
<span id="f_span">some simple</span>
<script>
var x = document.getElementById("f_span");
</script>
Then you can check if x value equals with the value that user had selected.
You can use setInterval to get the selected text every x second. With the joined function you can get the selected text.
For the selection of user :
function getSelected() {
if(window.getSelection) { return window.getSelection(); }
else if(document.getSelection) { return document.getSelection(); }
else {
var selection = document.selection && document.selection.createRange();
if(selection.text) { return selection.text; }
return false;
}
return false;
}
It return an object that give you the offset of the selection. If result.anchorOffset = 0 and result.focusOffset = result.anchorNode.length (if it start at the begining of the node and it have the length of the whole node), then the user selected all your node.
Thanks for your replies, it allowed me to cobble together my solution:
function applyTextFormatClass(className) {
var selection = getSelected();
var parent = selection.getRangeAt(0).commonAncestorContainer; //see comment and link below
if (parent.nodeType !== 1) {
parent = parent.parentNode; //we want the parent node
}
var tagText = parent.innerText;
var selectText = selection.toString();
if (tagText.length !== selectText.length) {
addNodeAroundSelectedText(selection, className); //create new node
} else {
addClass(parent, className); //add class to existing node
}
}
commonAncestorContainer: https://developer.mozilla.org/en-US/docs/Web/API/Range/commonAncestorContainer

Javascript: How do I expand a user selection based on html tags?

Le Code:
http://jsfiddle.net/frf7w/12/
So right now, the current method will take the selected text exactly as... selected, and add tags so that when it is displayed, the page doesn't blow up.
But what I want to do:
Is to, when a user selects a portion of a page, if there are un-matched tags within the selection, the selection will either jump forward or backward (depending on what unmatched tag is in the selection) to the tag(s) that make the selection valid html.
The reason why I want to do this, is because I want a user to be able te select text on a page, and be able to edit that text in a WYSIWYG editor (I can currently do this with the linked code), and then put what they've edited back into the page (currently can't do this, because the method I use adds tags).
The coverAll method in this SO answer has exactly what you want Use javascript to extend a DOM Range to cover partially selected nodes. For some reason extending Selection prototype does not work for me on my chrome, so I extracted the code and substituted this with window.getSelection(). Final code looks like this:
function coverAll() {
var ranges = [];
for(var i=0; i<window.getSelection().rangeCount; i++) {
var range = window.getSelection().getRangeAt(i);
while(range.startContainer.nodeType == 3
|| range.startContainer.childNodes.length == 1)
range.setStartBefore(range.startContainer);
while(range.endContainer.nodeType == 3
|| range.endContainer.childNodes.length == 1)
range.setEndAfter(range.endContainer);
ranges.push(range);
}
window.getSelection().removeAllRanges();
for(var i=0; i<ranges.length; i++) {
window.getSelection().addRange(ranges[i]);
}
return;
}
You can change the boundaries of the selection by adding a range:
var sel = window.getSelection(),
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);
The above example will give you a selection that is expanded to cover the entire of the tree between the start and end nodes, inclusive (thanks to commonAncestorContainer()).
This treats text nodes as equal to dom elements, but this shouldn't be a problem for you.
Demo: http://jsfiddle.net/Nq6hr/2/
You should work with the nodes given by the selection. It seems extentNode and anchorNode represents the end and the beginning of nodes of the selection both can help you having the "full" selection. https://developer.mozilla.org/fr/DOM/Selection
For the inline editing you should give a try to contentEditable attribute. You can surround the elements of your selection with a span containing this attribute https://developer.mozilla.org/en/DOM/element.contentEditable

Insert value into TEXTAREA where cursor was

I have a textarea and a div with values. When I click on a value I insert it into textarea. I need it to be inserted where my cursor was in textarea. Why do I say WAS? Because when I move it out and click on a value to insert, I assume it looses focus in the text area.
So, my question is, is there a way to "remember" the latest cursor position within textarea and then insert my values at that position?
Perhaps it could be a char number in a string?.. Currently I add it like this:
input.val( function( i, val ) { return val + " " + myInsert + " "; } );
Also I use jQuery, perhaps I could use it?
I've written a cross-browser jQuery plug-in for dealing with the caret/selection in textareas and text inputs called Rangy inputs (terrible name, I really should think of a better one). A combination of methods from this and the techniques in Edgar Villegas Alvarado's answer should do the trick, although in IE, the focusout event fires too late and you'll need to use the proprietary beforedeactivate event instead:
var $textBox = $("#foo");
function saveSelection(){
$textBox.data("lastSelection", $textBox.getSelection());
}
$textBox.focusout(saveSelection);
$textBox.bind("beforedeactivate", function() {
saveSelection();
$textBox.unbind("focusout");
});
When inserting text later, the following will insert text at the previous cursor position (or overwrite the previously selected text, if the user had selected any):
var selection = $textBox.data("lastSelection");
$textBox.focus();
$textBox.setSelection(selection.start, selection.end);
$textBox.replaceSelectedText("Some new text");
See jsFiddle example here: http://jsfiddle.net/rQXrJ/1/
Here is a working example of what you are trying to do check it out at:
http://jsfiddle.net/J5Z2n/1/
Hello there my good friend....
how do you do
the jQuery:
function insertAt (myField, myValue, startSel, endSel) {
if (startSel || startSel == '0') {
var startPos = startSel;
var endPos = endSel;
myField.val(myField.val().substring(0, startPos)+ myValue+ myField.val().substring(endPos, myField.val().length));
}
else {
myField.val() += myValue;
}
}
// calling the function
var targetBox = $('textarea#insert'),
startSel,
endSel;
targetBox.bind('focusout', function() {
//insertAtCursor(this, 'how do you do');
startSel = this.selectionStart;
endSel = this.selectionEnd;
});
$("#myvalue").click(function() {
var myValue = $(this).text();
insertAt(targetBox, myValue, startSel, endSel);
});
The original function was borrowed from this post
http://alexking.org/blog/2003/06/02/inserting-at-the-cursor-using-javascript
That should give you a solid head start I hope. Cheers
If the caret (the cursor) is somewhere in the text field, it registers in Javascript as an empty selection. That is, the selectionStart and selectionEnd attributes are the same. You can use those attributes to detect the position of the caret.
Apparently selectionStart and selectionEnd are quite useful here. Check this out:
http://www.scottklarr.com/topic/425/how-to-insert-text-into-a-textarea-where-the-cursor-is/
You can use the jQuery Caret plugin to get/set the cursor position .
Example usage:
var cursorPosition = $("#textbox").caret().start);
You could 'store' this position like this:
$("#textbox").focusout(function(){
var cursorPosition = $(this).caret().start);
$(this).data("lastCursorPos", cursorPosition);
});
To retrieve it (on your div click event), do this:
var lastCursorPosition = $("#textbox").data("lastCursorPos");
Hope this helps. Cheers

how to select a text range in CKEDITOR programatically?

Problem:
I have a CKEditor instance in my javascript:
var editor = CKEDITOR.instances["id_corpo"];
and I need to insert some text programatically, and select some text range afterwards.
I already did insert text through
editor.insertHtml('<h1 id="myheader">This is a foobar header</h1>');
But I need to select (highlight) the word "foobar", programatically through javascript, so that I can use selenium to work out some functional tests with my CKEditor plugins.
UPDATE 1:
I've also tried something like
var selection = editor.getSelection();
var childs = editor.document.getElementsByTag("p");
selection.selectElement(childs);
But doesn't work at all!
How can I do that?
I think that
selection.selectRange()
could do the job, but I'could not figure out how to use it.
There are no examples over there :(
Get current selection
var editor = CKEDITOR.instances["id_corpo"];
var sel = editor.getSelection();
Change the selection to the current element
var element = sel.getStartElement();
sel.selectElement(element);
Move the range to the text you would like to select
var findString = 'foobar';
var ranges = editor.getSelection().getRanges();
var startIndex = element.getHtml().indexOf(findString);
if (startIndex != -1) {
ranges[0].setStart(element.getFirst(), startIndex);
ranges[0].setEnd(element.getFirst(), startIndex + findString.length);
sel.selectRanges([ranges[0]]);
}
You can also do the following:
get the current selection
var selection = editor.getSelection();
var selectedElement = selection.getSelectedElement();
if nothing is selected then create a new paragraph element
if (!selectedElement)
selectedElement = new CKEDITOR.dom.element('p');
Insert your content into the element
selectedElement.setHtml(someHtml);
If needed, insert your element into the DOM (it will be inserted into the current position)
editor.insertElement(selectedElement);
and then just select it
selection.selectElement(selectedElement);
Check out the selectElement() method of CKEDITOR.dom.selection.
http://docs.cksource.com/ckeditor_api/symbols/CKEDITOR.dom.selection.html
insert text at cursor point in ck editor
function insertVar(myValue) {
CKEDITOR.instances['editor1'].fire( 'insertText',myValue);
}
this is working for me

Categories

Resources