HTML document selection using javascript - javascript

I use this javascript code to get the currently highlighted selection.
var selection = window.getSelection()
If the highlight is a section of text all within a <div>, how can I get the offset from the beginning of the <div> and the length of the highlight? (the length is not just the length of the text, it should be the actual length of the html code for that text)

You can get the length of the selected HTML as follows:
function getSelectionHtml() {
var sel, html = "";
if (window.getSelection) {
sel = window.getSelection();
if (sel.rangeCount) {
var frag = sel.getRangeAt(0).cloneContents();
var el = document.createElement("div");
el.appendChild(frag);
html = el.innerHTML;
}
} else if (document.selection && document.selection.type == "Text") {
html = document.selection.createRange().htmlText;
}
return html;
}
alert(getSelectionHtml().length);

Related

how to add tooltip triggered on clicking any word of the paragraph?

function get_selection()
{
var txt = '';
if (window.getSelection)
{
txt = window.getSelection();
}
else if (document.getSelection)
{
txt = document.getSelection();
}
else if (document.selection)
{
txt = document.selection.createRange().text;
}
return txt;
}
$(document).dblclick(function(e)
{
var t = get_selection();
alert(t);
});
I wanted to use a JQuery plugin for toolbar(toolbar.js) inside the function where we obtain the word which is being double clicked,Is it possible? Please guide.
One way you might be able to do it is by wrapping the selected text in a <span/> tag in order to attach the plugin.
$(document).dblclick(function() {
var span = document.createElement('span');
var sel = document.getSelection();
if (sel && sel.rangeCount) {
var range = sel.getRangeAt(0).cloneRange();
// wrap text in span element
range.surroundContents(span);
sel.removeAllRanges();
sel.addRange(range);
// show tooltip
$(span).toolbar({
content: '#toolbar-options',
position: 'top'
// remove span when tooltip hides
}).on('toolbarHidden', function (e) {
$(span).contents().unwrap('span');
});
}
});
Demo
This should give you start as you'll need to make adjustments to determine if selected text is a word.

Delete particular Text before and after the selected text javascript

I want to delete some particular text before and after the selected text.For example if the text is:
<p>This is a <random>sentence</random> that i am writing<p>
If the user selects text,it should remove <random> and </random> from the text and text will be like this.
This is a sentence that i am writing.
If the user selects anything other than 'sentence',nothing will happen.
I know how to select a particular text but i dont know the next step on how to remove text before and after a particular text.Is it possible?
function replaceSelection() {
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);
var selectedText = range.toString();
var replacementText = selectedText.replace(/<\/?random>/, '');
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(replacementText);
} else {
// In IE 9 we need to use innerHTML of a temporary element
var div = document.createElement("div"), child;
div.innerHTML = replacementText;
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();
var selectedText = range.text;
var replacementText = selectedText.replace(/<\/?random>/, '')
range.pasteHTML(replacementText);
}
}
<div onmouseup="replaceSelection()"><p>This is a <random>sentence</random> that i am writing<p></div>

How to get formatted text if selection is inside tags

If I select (highlight with mouse) bold text, I want to have <b></b> tags around it (the same goes for <i>, <span style="color:red">, <sup>, etc...).
The problem appears when You starting to select bold words inside all-bold sentence. Logically if I selected bold text - I want to copy bold text to other place. But if selection doesn't cross formatting tags I loose format info...
Here is working fiddle example: https://jsfiddle.net/xt557ov5/1/
Select just word BOLD in first line & press button.
Now select all second line, and press a button.
If you want even more magic - select just word LINE, exactly from first letter to the last (four letters no spaces around) - You also will get LINE without any bold formatting around it.
Snippet :
$('#extract').on('click', function() {
extract();
});
function extract() {
var str = getSelectionHtml();
str = str.toString().replace(/</g, "<").replace(/>/g, ">");
console.log(str);
$('#result').empty().append(str);
}
function getSelectionHtml() {
var html = "";
if (typeof window.getSelection != "undefined") {
var sel = window.getSelection();
if (sel.rangeCount) {
var container = document.createElement("div");
for (var i = 0, len = sel.rangeCount; i < len; ++i) {
container.appendChild(sel.getRangeAt(i).cloneContents());
}
html = container.innerHTML;
}
} else if (typeof document.selection != "undefined") {
if (document.selection.type == "Text") {
html = document.selection.createRange().htmlText;
}
}
return html;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js"></script>
<button type="button" id="extract">Extract</button>
<br>
<br>
<div id="edit">
<br>
<b>Select here just word BOLD and press [Extract].</b>
<br>
<br>
<br>Now select all this <b>LINE</b>, and press [Extract].
</div>
<h4>Result:</h4>
<div id="result">
</div>
Maybe this would help: stackoverflow: Identify wether the selected text in a web page is bold or not
In order to apply it to your code I constructed this:
...
function selectionIsBold() {
var range, isBold = false;
if (window.getSelection) {
var sel = window.getSelection();
if (sel && sel.getRangeAt && sel.rangeCount) {
range = sel.getRangeAt(0);
document.designMode = "on";
sel.removeAllRanges();
sel.addRange(range);
}
}
if (document.queryCommandState) {
isBold = document.queryCommandState("bold");
}
if (document.designMode == "on") {
document.designMode = "off";
}
return isBold;
}
function extract(){
var str = getSelectionHtml();
if(selectionIsBold()){
str= "<b>"+str.toString()+"</b>";
}else{
str = str.toString().replace(/</g, "<").replace(/>/g, ">");
}
$('#result').empty().append(str);
}
...
Note: selectionIsBold returns also true when thr text is "<strong>"-tagged. For other text formation checks (e.g <i>) change
isBold = document.queryCommandState("bold");
to
isBold = document.queryCommandState("italic");

How to format HTML content or selection "correctly"?

I am playing with container <div>, which property contentEditable is set to "true". I was very happy to format selection of text this way:
Select text with mouse
Retrieve HTML of the selection
making new string: e.g. <font color = "red"> + HTML selection + </font>
Replace selected HTML with the string above
It seemed perfectly OK, but when applied this method several times, i recieved HTML text like that:
The oldest browsers <font color="Red">support only </font><font color="Red"><font color="Red">one<font color="Red"> way</font></font><font color="Red"> of</font> registering event handlers</font>, the way invented by Netscape.
and that is is really ugly. I would like to get nice formated piece like:
The oldest browsers <font color="Red">support only one way of registering event handlers</font>, the way invented by Netscape.
I tried apply selection.normalize(); before adding starting and ending tags, but it did not help.
What is the correct method changing HTML, in order to get "normal" result"?
thanks
Code sample:
<script>
function FontRed() {
embraceSelectionWithTag('<font color=Red>', '</font>');
}
function embraceSelectionWithTag(tag1,tag2) {
var range, html;
html = tag1 + getSelectionHtml() + tag2;
range = replaceSelectionWithHtml(html);
var selection = window.getSelection();
selection.removeAllRanges();
selection.addRange(range);
}
function getSelectionHtml() {
var html = "";
if (typeof window.getSelection != "undefined") {
var sel = window.getSelection();
if (sel.rangeCount) {
var container = document.createElement("div");
for (var i = 0, len = sel.rangeCount; i < len; ++i) {
container.appendChild(sel.getRangeAt(i).cloneContents());
}
container.normalize();
html = container.innerHTML;
return html;
}
} else if (typeof document.selection != "undefined") {
if (document.selection.type == "Text") {
//container.normalize();
html = document.selection.createRange().htmlText;
return html;
}
}
}
function replaceSelectionWithHtml(html) {
var range, html;
if (window.getSelection && window.getSelection().getRangeAt) {
range = window.getSelection().getRangeAt(0);
range.deleteContents();
var div = document.createElement("div");
div.innerHTML = html;
var frag = document.createDocumentFragment(), child;
while ( (child = div.firstChild) ) {
frag.appendChild(child);
}
range.insertNode(frag);
return range;
} else if (document.selection && document.selection.createRange) {
range = document.selection.createRange();
range.pasteHTML(html);
return range;
}
}
</script>
<body>
<div id="containter2" contentEditable = true>
The oldest browsers support only one way of registering event handlers, the way invented by Netscape.
</div>
<button onclick="FontRed()">Font Red</button>
</body>

Set caret position right after the inserted element in a contentEditable div

I'm inserting an element into a contentEditable div but the browser sets the position of the cursor before the inserted element. Is it possible to set the cursor right after the inserted element so that the user keeps typing without having to re-adjust the cursor position?
The following function will do it. DOM Level 2 Range objects make this easy in most browsers. In IE, you need to insert a marker element after the node you're inserting, move the selection to it and then remove it.
Live example: http://jsfiddle.net/timdown/4N4ZD/
Code:
function insertNodeAtCaret(node) {
if (typeof window.getSelection != "undefined") {
var sel = window.getSelection();
if (sel.rangeCount) {
var range = sel.getRangeAt(0);
range.collapse(false);
range.insertNode(node);
range = range.cloneRange();
range.selectNodeContents(node);
range.collapse(false);
sel.removeAllRanges();
sel.addRange(range);
}
} else if (typeof document.selection != "undefined" && document.selection.type != "Control") {
var html = (node.nodeType == 1) ? node.outerHTML : node.data;
var id = "marker_" + ("" + Math.random()).slice(2);
html += '<span id="' + id + '"></span>';
var textRange = document.selection.createRange();
textRange.collapse(false);
textRange.pasteHTML(html);
var markerSpan = document.getElementById(id);
textRange.moveToElementText(markerSpan);
textRange.select();
markerSpan.parentNode.removeChild(markerSpan);
}
}
Alternatively, you could use my Rangy library. The equivalent code there would be
function insertNodeAtCaret(node) {
var sel = rangy.getSelection();
if (sel.rangeCount) {
var range = sel.getRangeAt(0);
range.collapse(false);
range.insertNode(node);
range.collapseAfter(node);
sel.setSingleRange(range);
}
}
If you're inserting an empty div, p or span, I believe there needs to be "something" inside the newly created element for the range to grab onto -- and in order to put the caret inside there.
Here's my hack that seems to work OK in Chrome. The idea is simply to put a temporary string inside the element, then remove it once the caret is in there.
// Get the selection and range
var idoc = document; // (In my case it's an iframe document)
var sel = idoc.getSelection();
var range = sel.getRangeAt(0);
// Create a node to insert
var p = idoc.createElement("p"); // Could be a div, span or whatever
// Add "something" to the node.
var temp = idoc.createTextNode("anything");
p.appendChild(temp);
// -- or --
//p.innerHTML = "anything";
// Do the magic (what rangy showed above)
range.collapse(false);
range.insertNode( p );
range = range.cloneRange();
range.selectNodeContents(p);
range.collapse(false);
sel.removeAllRanges();
sel.addRange(range);
// Clear the non
p.removeChild(p.firstChild);
// -- or --
//p.innerHTML = "";
Here's what worked for me, using Rangy, in a VueJS context.
// When the user clicks the button to open the popup to enter
// the URL, run this function to save the location of the user's
// selection and the selected text.
newSaveSel: function() {
if (this.savedSel) {
rangy.removeMarkers(this.savedSel);
}
// Save the location of the selected text
this.savedSel = rangy.saveSelection();
// Save the selected text
this.savedSelText = rangy.getSelection().toString();
this.showLinkPopup = true;
console.log('newSavedSel', this.savedSel);
},
surroundRange: function() {
// Restore the user's selected text. This is necessary since
// the selection is lost when the user stars entering text.
if (this.savedSel) {
rangy.restoreSelection(this.savedSel, true);
this.savedSel = null;
}
// Surround the selected text with the anchor element
var sel = rangy.getSelection();
var range = sel.rangeCount ? sel.getRangeAt(0) : null;
if (range) {
// Create the new anchor element
var el = document.createElement("a");
el.style.backgroundColor = "pink";
el.href = this.anchorHref;
el.innerHTML = this.savedSelText;
if (this.checked) {
el.target = "_blank";
}
// Delete the originally selected text
range.deleteContents();
// Insert the anchor tag
range.insertNode(el);
// Ensure that the caret appears at the end
sel.removeAllRanges();
range = range.cloneRange();
range.selectNode(el);
range.collapse(false);
sel.addRange(range);
this.showLinkPopup = false;
}
},

Categories