I have some text in div selectable and I want to allow the user to remove text by selecting the text using their cursor.
It works fine, but if you click on the text, everything is removed. If you click and drag to select, it works fine.
What can I do to make it work with my text properly. If user clicks the text, it shouldn't remove everything.
jsFiddle for testing.
jQuery:
if (window.getSelection().toString() != "") {
selectedText = window.getSelection().toString()
var text1 = $(".selectable").text().split("")
pointStart = window.getSelection().anchorOffset
pointEnd = window.getSelection().focusOffset
if (pointEnd < pointStart) {
pointStart = pointEnd
}
text1.splice(pointStart, selectedText.length);
text1 = text1.join("")
} else {
selectedText = $(".selectable").text()
var text1 = ""
}
$(".selectable").text(text1)
Example HTML:
<div class="selectable">
Hello world what is 進撃の巨人 reality test test... test.
</div>
EDIT: Potential solution:
if (window.getSelection().toString() == "") {
return false;
}
Your error lies on the else part. If the user has not selected any text, which is the case of simply clicking on the text, your else code block gets executed.
In that block you assign var text1 = "";
Later you assing text1 as the text content of your $(".selectable") element.
Just change that line to:
var text1 = selectedText;
See the updated fiddle
I also suggest you declare var text1 only once at the top of your mouseup handler and change your assignments from var text1 = "value" to text1 = "value"
See: var hoisting
Related
First Time I am asking something here I hope I will be precise enough.
I am trying to make a simple extension for chrome that highlight the selected text when I press "H" on my keyboard, but I have some issue :
Use Case
The user select with his mouse a piece of text.
The user press H on his keyboard
Bibbidi-Bobbidi-Boo the selected text is highlighted.
Code so far
To detect when user press H and to get the piece of text he selected :
window.addEventListener('keydown', e => {
if(e.code == "KeyH")
{
var selected = window.getSelection()
//SimpleHighLight(selected);
ComplexHighLight(selected);
}
});
I have coded coded a simple way to do what I want like this :
function SimpleHighLight(selected){
var selectedText = selected.toString();
if(selectedText.length != 0)
{
var range = selected.getRangeAt(0);
var element = selected.anchorNode.parentNode;
var highlited = "<span style='background: rgb(255,255,0)'>" + selectedText + "</span>";
var reg = new RegExp(selectedText,"g");
var text = element.innerHTML.replace(reg, highlited);
element.innerHTML = text;
}
}
It work fine for piece of text in an unique DOM element and when there is no other occurrence of the selected text but I want it to always work, like in a case of my selected text comes from 2 different paragraphs.
So I did this :
function ComplexHighLight(selected){
var selectedText = selected.toString();
if(selectedText.length != 0)
{
console.log(" Selection : " + selectedText);
var range = selected.getRangeAt(0);
if(!range.collapsed)
{
var startNode = range.startContainer;
var startOffset = range.startOffset;
var endNode = range.endContainer;
var endOffset = range.endOffset;
if(startNode == endNode) //Means that its in the same node element
{
var highlited = "<span style='background: rgb(255,255,0)'>" + selectedText + "</span>";
startNode.replaceData(startOffset, endOffset-startOffset, highlited);
startNode.parentNode.innerHTML = startNode.nodeValue;
}
}
}
}
That's only a part of the problem where I handle when a piece of text is in the same element (I am already too much in trouble to handle when the selected text comes from multiples elements :( ).
Issue
On paper, it should work, but the main issue is that when I do :
startNode.parentNode.innerHTML = startNode.nodeValue;
the <span> division is given to innerHTML as a string and not some HTML stuff.
I have worked around this for about the whole evening but I can't fix it, does anyone have an idea of how I should do that ?
I have a textbox with multiple sentences, and when I click on a word, I need to get the position of the word in this textbox.
The code actually just get the whole text from the textarea and writes it in another text area. I am struggling to get specific position of the character (or word) I clicked on.
What it should do is to copy just the text before clicked word/character.
var themaintextarea = document.getElementById('textarea');
themaintextarea.addEventListener('click', replace_sentence);
function replace_sentence(e){
var themaintext = document.getElementById("textarea").innerHTML;
//alert(thereplace);
document.getElementById("curent_sentence").value = themaintext;
theselection = window.getSelection();
var therange = theselection.getRangeAt(0);
var sometext = therange.toString().trim();
alert(sometext);
}
<textarea id="curent_sentence"></textarea>
<div id="textarea" contenteditable>I <span>like</span> apples. I <span>like</span> apples.
<br>
I <span>like</span> apples.</div>
EDITED : Heres the fiddle to select the word before the word clicked on. Note: no spans are used in this. https://jsfiddle.net/Lnkav5ca/5/
I think i understood what you're looking for. https://jsfiddle.net/Lnkav5ca/1/ I put all the clickable words in a class and add event listeners to that class. So when the word is clicked on it gets inserted into the text box.
var themaintextarea = document.getElementsByClassName('clickable');
for (var i = 0; i < themaintextarea.length; i++) {
themaintextarea[i].addEventListener('click', replace_sentence);
}
function replace_sentence(e){
console.log(e);
var themaintext = e.target.innerHTML
//alert(thereplace);
document.getElementById("curent_sentence").value = themaintext;
theselection = window.getSelection();
var therange = theselection.getRangeAt(0);
var sometext = therange.toString().trim();
//alert(sometext);
}
I have a contenteditable div (with id 'editor1') that allows users to input text. There is then a function that allows them to color any highlighted text. My js uses window.getSelection().getRangeAt(0), but the issue with this is that they can highlight words outside of the div and their color will change as well. So far; I've tried:
function red(){
{
var getText = document.getElementById("editor1").innerHTML;
var selection = getText.getSelection().getRangeAt(0);
var selectedText = selection.extractContents();
var span = document.createElement("span");
span.style.color = "red";
span.appendChild(selectedText);
selection.insertNode(span);
}
}
Fiddle: https://jsfiddle.net/xacqzhvq/
As you can see, if I highlight "this will become red as well", I can use the button to make that red too.
How can I only color the highlighted text only within the editor1 div?
You are able to get the node element from the selection using .baseNode. From there you can get the parent node and use that for comparison.
function red(){
// If it's not the element with an id of "foo" stop the function and return
if(window.getSelection().baseNode.parentNode.id != "foo") return;
...
// Highlight if it is our div.
}
In the example below I made the div have an id that you can check to make sure it's that element:
Demo
As #z0mBi3 noted, this will work the first time. But may not work for many highlights (if they happen to get cleared). The <span> elements inside the div create a hierarchy where the div is the parent elements of many span elements. The solution to this would be to take traverse up through the ancestors of the node until you find one with the id of "foo".
Luckily you can use jQuery to do that for you by using their .closest() method:
if($(window.getSelection().baseNode).closest("#foo").attr("id") != "foo") return;
Here is an answer with a native JS implemented method of .closest().
Are you looking for this,
//html
<body>
<p id='editor1'>asdf</p>
<button onclick='red()'>
RED
</button>
</body>
//JavaScript
window.red = function(){
//var getText = document.getElementById("editor1").innerHTML;
var selection = window.getSelection().getRangeAt(0);
var selectedText = selection.extractContents();
var span = document.createElement("span");
span.style.color = "red";
span.appendChild(selectedText);
selection.insertNode(span);
}
Plunker: https://plnkr.co/edit/FSFBADoh83Pp93z1JI3g?p=preview
Try This Code :
function addBold(){
if(window.getSelection().focusNode.parentElement.closest("#editor").id != "editor") return;
const selection = window.getSelection().getRangeAt(0);
let selectedParent = selection.commonAncestorContainer.parentElement;
let mainParent = selectedParent;
if(selectedParent.closest("b"))
{
//Unbold
var text = document.createTextNode(selectedParent.textContent);
mainParent = selectedParent.parentElement;
mainParent.insertBefore(text, selectedParent);
mainParent.removeChild(selectedParent);
mainParent.normalize();
}
else
{
const span = document.createElement("b");
span.appendChild(selection.extractContents());
selection.insertNode(span);
mainParent.normalize();
}
if (window.getSelection) {
if (window.getSelection().empty) { // Chrome
window.getSelection().empty();
} else if (window.getSelection().removeAllRanges) { // Firefox
window.getSelection().removeAllRanges();
}
} else if (document.selection) { // IE?
document.selection.empty();
}
};
<div id="editor" contenteditable="true">
You are the programmers of the future
</div>
<button onclick="addBold()">Bold</button>
I got the code and added my edits from those following answers :
Bold/unbold selected text using Window.getSelection()
getSelection().focusNode inside a specific id doesn't work
To be a bit more specific: A user should be able to highlight text, so that Javascript could replace this highlighted part with a text-area. My problem is, that i don't know how to edit any part of the text!
I hope my example drawing helps to understand my very specific question!
I have built a little script in JSFiddle here is the link: http://jsfiddle.net/WT5XE/2/
JS
var textB = ''; // text before split
var textA = ''; // text after split
...
// here starts the magic, split current text and insert a new input box
var splitted = that.innerHTML.split(text);
textB = splitted[0];
textA = splitted[1];
$('#text').empty();
// Append the text before in a new span
$('#text').append('<span>'+textB+'</span>');
// Create the input box and attach key listener (on enter it will terminate and set the new text)
$('<input value="'+text+'">')
.keypress(function (e) {
if (e.which == 13) {
isInput = false;
var newText = this.value;
var text = textB+newText+textA;
$('#text').empty();
$('#text').append(text);
return false;
}
})
.appendTo('#text');
// Append the text after in a new span
$('#text').append('<span>'+textA+'</span>');
My end goal is for the user to be able to :
select text from a paragraph
wrap the text in a span
place an action button / div at the end of the selection that they could click to take further action
Here is my code so far:
function Discussion(){
var $this = this;
$(document).bind("mouseup",function(){
$this.selectText($this);
});
}
Discussion.prototype.selectText = function($this){
var selection = $this.getSelectedText();
if(selection.length > 3){
console.log(selection);
var spn = '<span class="selected">' + selection + '</span>';
$(this).html($(this).html().replace(selection, spn));
//ERROR here; it says that it can't replace() on undefined $(this).html()
}
}
Discussion.prototype.getSelectedText = function(){
if(window.getSelection){
return window.getSelection().toString();
}
else if(document.getSelection){
return document.getSelection();
}
else if(document.selection){
return document.selection.createRange().text;
}
}
As you might expect, so far I can get the text of the selection with window.getSelection().toString(). If I remove the .toString(), I get a Selection object. With this I can use window.getSelection().anchorNode.parentNode to get most of the information I need.
I also see that Selection.anchorOffset and Selection.extentOffset will give me the range of characters I've selected. Is there a way I can use this information to place the span?
I guess my questions would be:
Why isn't this code wrapping the selection in divs?
Will this fail with multiple instances of the same text?
After wrapping the selection with a span or inline-block div or something, will I be able to get (and use) its position for positioning an additional button / div?
Phew, thanks for your time!
Edit: I added a JS fiddle here : http://jsfiddle.net/nn62G/
I'm going to post my js fiddle here with the solution for posterity. Thanks for all the help, everyone!
http://jsfiddle.net/prodikl/mP8KT/
function Discussion(){
var $this = this;
$(document).bind("mouseup",function(){
$this.selectText($this);
});
}
Discussion.prototype.selectText = function($this){
$("mark").contents().unwrap();
$("mark").remove();
var selection = $this.getSelection();
var range = selection.getRangeAt(0);
var cssclass = $(selection.anchorNode.parentNode).attr("class");
if(selection.toString().length > 2){
$this.startPoint = selection.anchorOffset;
$this.endPoint = selection.extentOffset;
var newNode = document.createElement("mark");
range.surroundContents(newNode);
$("mark").attr('title', ' ');
$("mark").tooltip({
content : "<a class='content' href='http://www.google.com' target='_blank'>Here's a link.</a>"
});
$("mark").on('mouseleave',function(event){
event.stopImmediatePropagation();
}).tooltip("open");
}
}
Discussion.prototype.getSelection = function(){
if(window.getSelection){
return window.getSelection();
}
else if(document.getSelection){
return document.getSelection();
}
else if(document.selection){
return document.selection.createRange().text;
}
}
var discussion = new Discussion();
The value returned by getSelectedText is a selection not an element in the DOM. That is why your cal to the html function is failing, selection has no such property.
You can do what you want as follows, set the part of the document you want to process as contentEditable for example a <p> or <div>. When you select within that area then you can get the HTML element using document.activeElement. If you select outside a contentEdiatble area then activeElement returns the body element. The activeElement you CAN process :
document.activeElement.innerHTML =
document.activeElement.innerHTML.replace (selection, spn);
However, if you make parts of your document editable other things happen which you may not want. More seriously, if the selected text occurs multiple times you cannot determine which one has actually been selected so this may not be a solution to your problem.
Updated fiddle here
Ok well, a possible solution could be to find the element that contains the current selection and replace it's html something like:
// this get's you all the elements (including the top parent) that contain the selection
var existsInElements = $('*:contains("' + selection + '")');
// the exact element match is in the last one
var exactElement = existsInElements[existsInElements.length - 1];
Here is a fiddle that works.