Can we change the HTML or its attributes of the selected part of the web page using javascript?
For example,
There is a random web page:(A part of it is shown)
with HTML as
...<p>Sample paragraph</p>..
It is possible to get the HTML of the selected text which has already been answered here.
But, is it possible for me change the html of the selected text? Like, add a class or id attribute to the paragraph tag.
The jQuery wrapselection plugin sounds like what you're looking for http://archive.plugins.jquery.com/project/wrapSelection . What you're asking for is not directly possible though, the plugin works by adding in extra nodes around the surrounding text which may not be 100% reliable (particularly in IE6-8)
Here is a working example without a plugin.
http://jsfiddle.net/9HTPw/2/
function getSel() {
var sel = null;
if (
typeof document.selection != "undefined" && document.selection
&& document.selection.type == "Text") {
sel = document.selection;
} else if (
window.getSelection && window.getSelection().rangeCount > 0) {
sel = window.getSelection();
}
return sel;
}
var getSelectionStart = (function () {
function createRangeFromSel(sel) {
var rng = null;
if (sel.createRange) {
rng = sel.createRange();
} else if (sel.getRangeAt) {
rng = sel.getRangeAt(0);
if (rng.toString() == "") rng = null;
}
return rng;
}
return function (el) {
var sel = getSel(),
rng, r2, i = -1;
if (sel) {
rng = createRangeFromSel(sel);
if (rng) {
if (rng.parentElement) {
if (rng.parentElement() == el) {
r2 = document.body.createTextRange();
r2.moveToElementText(el);
r2.collapse(true);
r2.setEndPoint("EndToStart", rng);
i = r2.text.length;
}
} else {
if ( rng.startContainer && rng.endContainer
&& rng.startContainer == rng.endContainer
&& rng.startContainer == rng.commonAncestorContainer
&& rng.commonAncestorContainer.parentNode == el) {
//make sure your DIV does not have any inner element,
//otherwise more code is required in order to filter
//text nodes and count chars
i = rng.startOffset;
}
}
}
}
return i;
};
})();
$("#content").on('mousedown', function () {
$("#content").html(content)
});
var content = $("#content").html();
$("#content").on('mouseup', function () {
var start = getSelectionStart(this);
var len = getSel().toString().length;
$(this).html(
content.substr(0, start) +
'<span style=\"background-color: yellow;\">' +
content.substr(start, len) +
'</span>' +
content.substr(Number(start) + Number(len), content.toString().length));
});
References:
http://bytes.com/topic/javascript/answers/153164-return-selectionstart-div
Related
I've a div with contenteditable and I'm trying to manipulate the array of this div to check if each letter written inside it, is the same as the letter on another text, based on the index.
The validation is done, I can check it, my problem is that, I need to change the letter color to red, if the letter don't validate. And I'm having trouble inserting a span with the class that I want.
Anyone have a sugestion?
Code follows:
timer.addEventListener('secondsUpdated', function (e) {
$('#countdown .values').html(timer.getTimeValues().toString(['minutes', 'seconds']));
if ($('.true-textarea').contents().get(0)) {
var textResult = $(".true-textarea").contents().get(0).nodeValue;
var textSize = textResult().length - 1;
}
$(".true-textarea").on("keypress", function(e) {
var c = String.fromCharCode(e.which);
e.preventDefault();
$(".true-textarea").html("");
if (c != textFake[textSize]) {
$(".true-textarea").addClass("red-border");
cls = "color-red";
} else {
cls = "color-purple";
$(".true-textarea").removeClass("red-border");
}
console.log('result', textResult);
console.log('size', textSize)
res += "<span class='"+cls+"'>"+c+"</span>";
cls = "";
$(".true-textarea").html(res);
});
});
So after hours trying to figure this out I came up if this code.
Used some examples that I saw in around the internet, fixed some problems, and it's working with the following code:
function focusAtEnd(el) {
el.focus();
if (typeof window.getSelection != "undefined"
&& typeof document.createRange != "undefined") {
var range = document.createRange();
range.selectNodeContents(el);
range.collapse(false);
var sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);
} else if (typeof document.body.createTextRange != "undefined") {
var textRange = document.body.createTextRange();
textRange.moveToElementText(el);
textRange.collapse(false);
textRange.select();
}
}
$('.true-textarea').unbind();
$('.true-textarea').focus();
timer.start({countdown: true, startValues: {seconds: 60}});
$('#countdown .values').html(timer.getTimeValues().toString(['minutes', 'seconds']));
var res = "", cls = "";
textResult.on("keypress", function(e){
textResult.find('br').remove();
var c = String.fromCharCode(e.which);
e.preventDefault();
res = textResult.html();
textSize = textResult.text().length;
if (c === textFake[textSize]) {
cls = "color-purple";
} else {
cls = "color-red";
}
res += "<span class='"+cls+"'>"+c+"</span>";
var del = false;
var lastFocused;
cls = "";
textResult.html(res);
focusAtEnd($(this).get(0));
})
window.addEventListener("keydown", function(e){
/*
keyCode: 8
keyIdentifier: "U+0008"
*/
if(e.keyCode === 16 && getSelectionText() != "") {
e.preventDefault();
replaceSelectedText(strcon(getSelectionText()));
}
});
function getSelectionText() {
var text = "";
if (window.getSelection) {
text = window.getSelection().toString();
} else if (document.selection && document.selection.type != "Control") {
text = document.selection.createRange().text;
}
return text;
}
function strcon(givenString) {
var b = '';
var a = givenString;
for (i = 0; i < a.length; i++) {
if (a.charCodeAt(i) >= 65 && a.charCodeAt(i) <= 90) {
b = b + a.charAt(i).toLowerCase();
}
else
b = b + a.charAt(i).toUpperCase();
}
return b;
}
function replaceSelectedText(replacementText) {
var sel, range;
if (window.getSelection) {
sel = window.getSelection();
if (sel.rangeCount) {
range = sel.getRangeAt(0);
range.deleteContents();
range.insertNode(document.createTextNode(replacementText));
}
} else if (document.selection && document.selection.createRange) {
range = document.selection.createRange();
range.text = replacementText;
}
}
The code I have right now seems to change the appearance of the actual text instead of actually changing it. For example, when I'm on Facebook and I press the certain key, the text seems to have changed but then when I press enter, the text goes back to what it was before.
I believe the issue is with the function replaceSelectedText but I'm not sure how to fix it.
Any ideas?
No JQuery please.
https://jsfiddle.net/1rvz3696/
You have to get your textarea element to replace the value in it. This is how your replaceSelectedText function should look like,
function replaceSelectedText(text) {
var txtArea = document.getElementById('myTextArea');
if (txtArea.selectionStart != undefined) {
var startPos = txtArea.selectionStart;
var endPos = txtArea.selectionEnd;
selectedText = txtArea.value.substring(startPos, endPos);
txtArea.value = txtArea.value.slice(0, startPos) + text + txtArea.value.slice(endPos);
}
}
And here's the Fiddle.
Without specific id, you can replace txtArea to this.
var txtArea = document.activeElement;
And another Fiddle
I was testing out the getSelection() method, and I wanted my program to get the selected text in a certain paragraph of text and display it in a div tag. I used the following code:
var txt = document.getSelection();
document.getElementById("display").innerHTML = "The text you have selected is: " + txt + ".";
However, I want the program to only get selections made in the paragraph itself, not in the entire document. I tried using document.getElementById("id").getSelection(); but it didn't work.
How can I make it so getSelection() only applies to a certain element?
Here's one approach, tested only in Chromium 19 (Which supports textContent, for Internet Explorer innerText would have to be used instead):
function getSelectedText() {
if (window.getSelection) {
return window.getSelection().toString();
} else if (document.selection) {
return document.selection.createRange().text;
}
return '';
}
var b = document.getElementsByTagName('body')[0],
o = document.getElementById('output');
b.onmouseup = function(e){
var selText = getSelectedText(),
targetElem = e.target.tagName.toLowerCase();
if (selText && targetElem == 'p') {
o.textContent = 'You selected the text: "' + selText + '" from a ' + targetElem + ' element.';
}
};
JS Fiddle demo.
getSelection() is only available as a method of window and document. If you want to get a Range representing just the portion of the user selection that lies within a particular node, here's a function to do that, using my Rangy library (the code to do without the library would be longer and is beyond my enthusiasm to write right now):
function getSelectedRangeWithin(el) {
var selectedRange = null;
var sel = rangy.getSelection();
var elRange = rangy.createRange();
elRange.selectNodeContents(el);
if (sel.rangeCount) {
selectedRange = sel.getRangeAt(0).intersection(elRange);
}
elRange.detach();
return selectedRange;
}
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;
}
written in coffeescript:
getSelected = ->
if window.getSelection
return window.getSelection()
else if document.getSelection
return document.getSelection()
else
selection = document.selection and document.selection.createRange()
return selection.text if selection.text
return false
false
delicious javascript
Okay, I've been looking all over the web to find a solution but I couldn't find one, is there a way to get the word before the caret position in an editable div so a bit like:
This is some| demo texts
This should return the word "some"... I don't know if this is possible, I would be glad for any help, thanks :).
With using Caret Position finder method provided here this will do what you want.
function ReturnWord(text, caretPos) {
var index = text.indexOf(caretPos);
var preText = text.substring(0, caretPos);
if (preText.indexOf(" ") > 0) {
var words = preText.split(" ");
return words[words.length - 1]; //return last word
}
else {
return preText;
}
}
function AlertPrevWord() {
var text = document.getElementById("textArea");
var caretPos = GetCaretPosition(text)
var word = ReturnWord(text.value, caretPos);
if (word != null) {
alert(word);
}
}
function GetCaretPosition(ctrl) {
var CaretPos = 0; // IE Support
if (document.selection) {
ctrl.focus();
var Sel = document.selection.createRange();
Sel.moveStart('character', -ctrl.value.length);
CaretPos = Sel.text.length;
}
// Firefox support
else if (ctrl.selectionStart || ctrl.selectionStart == '0')
CaretPos = ctrl.selectionStart;
return (CaretPos);
}
<input id="textArea" type="text" />
<br />
<input id="Submit" type="submit" value="Test" onclick="AlertPrevWord()" />
Here is also a jsfiddle.
Here is a rough method using the Selection and Range objects.
function getWord() {
var range = window.getSelection().getRangeAt(0);
if (range.collapsed) {
text = range.startContainer.textContent.substring(0, range.startOffset+1);
return text.split(/\b/g).pop();
}
return '';
}
You can see it in action here: http://jsfiddle.net/ggfFw/1/.
This will not work in IE. If you need IE support look at the Rangy library.
I had something like that https://stackoverflow.com/a/9960262/986160 yet at some point it wasn't getting a selection in my Chrome browser. Based on my other answer here: https://stackoverflow.com/a/26728677/986160 - I changed it accordingly to be:
function getLastWordBeforeCaret() {
const containerEl = document.getElementById('element-id');
let preceding = '';
let sel;
let range;
if (window.getSelection) {
sel = window.getSelection();
if (sel && sel.rangeCount > 0) {
range = sel.getRangeAt(0).cloneRange();
range.collapse(true);
range.setStart(containerEl, 0);
preceding = range.toString();
}
}
let queryMatch = preceding.match(/([^\s]+)$/i);
if (queryMatch) {
return queryMatch[1];
} else {
return '';
}
}
have this html:
<div id="editable" contentEditable="true" >
<span contentEditable="false" >Text to delete</span>
</div>
need that the span (and all text inside) is removed with a single backspace, is it possible?
This turned out to be more complicated than I thought. Or I've made it more complicated than it needs to be. Anyway, this should work in all the big browsers:
function getLastTextNodeIn(node) {
while (node) {
if (node.nodeType == 3) {
return node;
} else {
node = node.lastChild;
}
}
}
function isRangeAfterNode(range, node) {
var nodeRange, lastTextNode;
if (range.compareBoundaryPoints) {
nodeRange = document.createRange();
lastTextNode = getLastTextNodeIn(node);
nodeRange.selectNodeContents(lastTextNode);
nodeRange.collapse(false);
return range.compareBoundaryPoints(range.START_TO_END, nodeRange) > -1;
} else if (range.compareEndPoints) {
if (node.nodeType == 1) {
nodeRange = document.body.createTextRange();
nodeRange.moveToElementText(node);
nodeRange.collapse(false);
return range.compareEndPoints("StartToEnd", nodeRange) > -1;
} else {
return false;
}
}
}
document.getElementById("editable").onkeydown = function(evt) {
var sel, range, node, nodeToDelete, nextNode, nodeRange;
evt = evt || window.event;
if (evt.keyCode == 8) {
// Get the DOM node containing the start of the selection
if (window.getSelection && window.getSelection().getRangeAt) {
range = window.getSelection().getRangeAt(0);
} else if (document.selection && document.selection.createRange) {
range = document.selection.createRange();
}
if (range) {
node = this.lastChild;
while (node) {
if ( isRangeAfterNode(range, node) ) {
nodeToDelete = node;
break;
} else {
node = node.previousSibling;
}
}
if (nodeToDelete) {
this.removeChild(nodeToDelete);
}
}
return false;
}
};
Because you want to delete the whole element, it's better to make it contenteditable="false" so that browser won't let the contents of an element to be deleted.
Then you can use this attribute for tests in event handler as follows:
$('#editable').on('keydown', function (event) {
if (window.getSelection && event.which == 8) { // backspace
// fix backspace bug in FF
// https://bugzilla.mozilla.org/show_bug.cgi?id=685445
var selection = window.getSelection();
if (!selection.isCollapsed || !selection.rangeCount) {
return;
}
var curRange = selection.getRangeAt(selection.rangeCount - 1);
if (curRange.commonAncestorContainer.nodeType == 3 && curRange.startOffset > 0) {
// we are in child selection. The characters of the text node is being deleted
return;
}
var range = document.createRange();
if (selection.anchorNode != this) {
// selection is in character mode. expand it to the whole editable field
range.selectNodeContents(this);
range.setEndBefore(selection.anchorNode);
} else if (selection.anchorOffset > 0) {
range.setEnd(this, selection.anchorOffset);
} else {
// reached the beginning of editable field
return;
}
range.setStart(this, range.endOffset - 1);
var previousNode = range.cloneContents().lastChild;
if (previousNode && previousNode.contentEditable == 'false') {
// this is some rich content, e.g. smile. We should help the user to delete it
range.deleteContents();
event.preventDefault();
}
}
});
demo on jsfiddle