I am using contenteditable=true trying make a text editor. I have successfully used getSelection() to wrap the selection inside HTML tags.
A problem is though, that if the user selects text outside the editor and the operation button is clicked, that text will be wrapped inside tags as well.
How would I do it with getSelection(), check if the selection is inside a div with the class of editor?
Edit:
Currently using this code:
var sel = window.getSelection ? window.getSelection() : document.selection.createRange();
if(sel.getRangeAt){
var range = sel.getRangeAt(0);
var newNode = document.createElement("p");
newNode.setAttribute('class', operationClass);
range.surroundContents(newNode);
} else {
sel.pasteHTML('<p class="' + operationClass +'">'+sel.htmlText+'</p>');
}
Once you have var range = sel.getRangeAt(0); you can determine if range.commonAncestorContainer or one of its ancestors is the editor container, using code like this:
var ancestor = range.commonAncestorContainer;
while (ancestor.id != "editor" // Check id, class or otherwise
&& ancestor.parentElement != null) {
ancestor = ancestor.parentElement;
}
if (ancestor.id == "editor") {
// Selection is within the editor.
}
Related
As I know, the Evernote editor is the Rich Text Editor and differences between the input or textarea tag, so I can not use the following code to judge the type of it, is there a method to get the current element of Evernote editor where the cursor is by JavaScript instead of jQuery?
e.target.localName.toLowerCase() === 'input' || e.target.localName.toLowerCase() === 'textarea'
Evernote Editor is one of WYSIWYG HTML Editor
You can use WYSIWYG editor with attribute contentEditable="true" on html tag
For get current element on cursor, you can find answer on Get element node at caret position (in contentEditable)
Or I have some simple code that I use on my project
getCaretNode() {
let selection: any;
if (window.getSelection) {
selection = window.getSelection();
} else if (document.getSelection() && document.getSelection().type !== "Control") {
selection = document.getSelection();
}
let anchorNode = selection.anchorNode;
if (anchorNode === null) {
return null;
}
if (anchorNode.nodeType === 3) {
anchorNode = anchorNode.parentNode;
}
return anchorNode;
}
My current solution is:
Get selected html (include text and html tag), namely: selText
highlightText = <span>selText</span>
Find selText in innerHTML of the body or document (or the element which the mouse dragged in)
Replace with highlightText
But if the document is: a a a a a a and user selects the last a. My function will highlight the first or all a.
Any suggestion?
Thank you.
i think your question is duplicated, anyway i just searched the internet and found this article.
Below the final code to achieve what you ask
function highlightSelection() {
var selection;
//Get the selected stuff
if(window.getSelection)
selection = window.getSelection();
else if(typeof document.selection!="undefined")
selection = document.selection;
//Get a the selected content, in a range object
var range = selection.getRangeAt(0);
//If the range spans some text, and inside a tag, set its css class.
if(range && !selection.isCollapsed)
{
if(selection.anchorNode.parentNode == selection.focusNode.parentNode)
{
var span = document.createElement('span');
span.className = 'highlight-green';
range.surroundContents(span);
}
}
}
I also found this library rangy that is an helper you can use to select text but only works with jquery so i prefer the first vanilla-js solution.
var el = $("<span></span>");
el.text(rangy.getSelection().getRangeAt(0).toString());
rangy.getSelection().getRangeAt(0).deleteContents();
rangy.getSelection().getRangeAt(0).insertNode(el.get(0));
rangy.getSelection().getRangeAt(0).getSelection().setSingleRange(range);
On Range and User Selection
You have to select range using Document.createRange that return a Range object before you can use Range.surroundContents(), you could create a range this way.
var range = document.createRange();
range.setStart(startNode, startOffset);
range.setEnd(endNode, endOffset);
In practice you follow this guide to understand range and selection tecniques.
The most important point is contained in this code
var userSelection;
if (window.getSelection) {
userSelection = window.getSelection();
}
else if (document.selection) { // should come last; Opera!
userSelection = document.selection.createRange();
}
After this you can use
userSelection.surroundContents()
I would like to select text automatically (I mean something with the Ranges) in some div to in order to unit test my script in JS.
My problem is that I want to be able to select text in different ways here are some examples of selection (selection represented by [ for start and ] for end)
<div>[ here I select all the text in the div ]</div
<div>This is just a <b>piece [ of</b> selection]</div>
<div><ul><li>Also with [list</li><li>And why not select many items ?</li></ul><p>With a p ?] but not the complete p !</p>
I want to do that because the user can do that and so I should test every cases of use. (If I don't, the unit test is quite useless...)
Do you have an idea about how to select what I want ? (if possible...)
I found this on the web.
This a function that when called on a particular HTML element will auto-select all of its inner text. The function works on elements such as INPUT (text), TEXTAREA, DIV, SPAN, TD, and PRE. Cross browser compatible.
var autoSelect = function(event) {
var event = event || window.event,
elem = event.target || event.srcElement,
tag = (elem.tagName || "").toLowerCase(),
sel, range;
if (tag === "textarea" || tag === "input") {
try {
elem.select();
} catch(e) {
// May be disabled or not selectable
}
} else if (window.getSelection) { // Not IE
sel = window.getSelection();
range = document.createRange();
range.selectNodeContents(elem);
sel.removeAllRanges();
sel.addRange(range);
} else if (document.selection) { // IE
document.selection.empty();
range = document.body.createTextRange();
range.moveToElementText(elem);
range.select();
}
};
To attach this function to an element:
document.getElementById("foobar").onclick = autoSelect;
You can do it using a regex:
var divStr = document.getElementById('yourDiv').innerHTML;
var matchs = /\[(.*?)\]/.exec(divStr);
console.log(matchs[1]);
We get the inner html string, then parses everything is between [ ], and finnaly get the grouped content.
I'm using this solution by Tim Down to get selected html in a contenteditable div, and it's working fine (thank you Tim!)
But using Chrome, if I select a html string exactly at the boundaries of a html tag, as in this image: http://i.imgur.com/UiYzrcp.png?1:
what I get it's just plain text (test in this case).
If I expand the selection to a next character (letter c for example), instead I get the correct html (<strong>test</strong> c).
Can I get the full html in Webkit by selecting a word like in the image?
Thanks
Not really. WebKit normalizes each boundary of any range when it's added to the selection so that it conforms to WebKit's idea of valid selection/caret positions in the document. You could change the original function so that it detects the case of a selection containing all the text within an element and expanding the selection range to surround that element (without actually changing the selection). Here's a simple example (you may need something cleverer for a more general case, such as when the text is inside nested elements, detecting block/inline elements, etc.):
Demo: http://jsfiddle.net/btLeg/
Code:
function adjustRange(range) {
range = range.cloneRange();
// Expand range to encompass complete element if element's text
// is completely selected by the range
var container = range.commonAncestorContainer;
var parentElement = container.nodeType == 3 ?
container.parentNode : container;
if (parentElement.textContent == range.toString()) {
range.selectNode(parentElement);
}
return range;
}
function getSelectionHtml() {
var html = "", sel, range;
if (typeof window.getSelection != "undefined") {
sel = window.getSelection();
if (sel.rangeCount) {
var container = document.createElement("div");
for (var i = 0, len = sel.rangeCount; i < len; ++i) {
range = adjustRange( sel.getRangeAt(i) );
container.appendChild(range.cloneContents());
}
html = container.innerHTML;
}
} else if (typeof document.selection != "undefined") {
if (document.selection.type == "Text") {
html = document.selection.createRange().htmlText;
}
}
return html;
}
I'm using the rangy library and can select text in a content editable as follows:
var sel = rangy.getSelection();
alert(sel);
I can't figure out how to get the selected text parent node/element. For example, if I'm selecting text that is
<strong>My Text</strong>
or
<h1>My Title</h1>
how can I include the strong node or H1 element also?
sel.anchorNode.parentNode will get you the parent node of the node containing only one end of the selection. To get the innermost containing element for the whole selection, the easiest thing is to get a Range from the selection and look at its commonAncestorContainer property (which may be a text node, in which case you need to get its parent):
var sel = rangy.getSelection();
if (sel.rangeCount > 0) {
var range = sel.getRangeAt(0);
var parentElement = range.commonAncestorContainer;
if (parentElement.nodeType == 3) {
parentElement = parentElement.parentNode;
}
}