Position changes every time with window.getSelection() - javascript

Can anyone help me? I got these codes here https://stackoverflow.com/a/17836828/2338164
$(document).on("mouseup",".wrap",function(){
var highlight = window.getSelection();
if(highlight.toString().length>=1){
var spn = '<span class="highlight">' + highlight + '</span>';
var text = $(this).text();
var range = highlight.getRangeAt(0);
var startText = text.substring(0, range.startOffset);
var endText = text.substring(range.endOffset, text.length);
$('#q3txt').append(range.startOffset+"<br>");
$(this).html(startText + spn + endText);
}
});
I tried to use it and it's working fine, until you highlight again...
Here's a link http://jsfiddle.net/AN76g/.
What im trying to do is... user will highlight a block then wrap it in span, but if the user made a mistake and tries to highlight again, the span is removed and will try to wrap the new highlighted text. But either the position changes or parts of the text are being appended.

See this update: jsfiddle.
First, on the mousedown, you can unwrap the span as so:
$(document).on("mousedown",".wrap",function(){
$('.highlight').contents().unwrap();
});
Secondly, the problem with using the range.startOffset and range.endOffset is that you the start is relative to the containing element which could be the highlight span which causes you to replace the incorrect text on subsequent selections. Instead, replace the selection with the span as so:
$(document).on("mouseup",".wrap",function(){
var highlight = window.getSelection();
if(highlight.toString().length>=1){
var range = highlight.getRangeAt(0);
var selectionContents = range.extractContents();
var spn = document.createElement("span");
spn.className='highlight';
spn.appendChild(selectionContents);
range.insertNode(spn);
highlight.removeAllRanges();
}
});
Information from MDN Range.startOffset, specifically:
the startContainer is a Node of type Text, Comment, or CDATASection, then the offset is the number of characters from the start of the startContainer to the boundary point of the Range. For other Node types, the startOffset is the number of child nodes between the start of the startContainer and the boundary point of the Range.
Also, this answer.

Related

Clone/Mimic selection range

Selection does exactly what I need: Set a selection while the TTS reads the part, highlight the next part, and so on... the kicker is: The user must be able to change the selection color. I can set this in css through ::selection, but I'm unable to change it while the extension is running. The selection color also blends with the word highlighting color within, changing z-index in css doesn't change this blending.
What it should look like:
To circumvent this, I have written following code which sets begin and end of the range to mimic this behaviour but with full (color) control, although when applying the style to the parentElement of every node, in some cases, it will apply to a div or p, highlighting a whole portion of the text instead of the sentence:
(Don't mind the sentence algorithm, it's of no importance right now)
//Start a range for the highlighting
let range = rangy.createRange();
//Set the range on the first element with an offSet of 0
range.setStart(document.getElementById('ali' + i), 0);
while (
document.getElementById('ali' + i) !== null) {
let element = document.getElementById('ali' + i);
senArray.push(element.innerText);
wordIdArray.push(i);
if (
element.innerText.indexOf('.') >= 0
|| element.innerText.indexOf('?') >= 0
|| element.innerText.indexOf('!') >= 0
) {
break;
}
i++;
}
//Set the end of the range by getting the last element and its last offSet
let endOffset = document.getElementById('ali' + i).childNodes.length;
range.setEnd(document.getElementById('ali' + i), endOffset);
console.log(range);
//get nodes from range and apply style to parent element
let nodes = range.getNodes();
nodes.forEach(function (el) {
el.parentNode("style", "background-color: " + readingUnitColor + ";")
});
//Make selection object, remove existing ranges (to be sure) and add the range to the selection object.
// let selection = window.getSelection();
// selection.removeAllRanges();
// selection.addRange(range);
I'm using rangy here, but I had the same results with native JS by using:
let CAContainer = range.commonAncestorContainer;
CAContainer.setAttribute("style", "background-color: " + readingUnitColor + ";");
I also tried wrapping the sentences in a span, but whenever I add '<span id="id">' to the setStart part of the range, chrome will auto-close that tag immediately after the first node, regardless of '</span>' being at the setEnd of the range.
Any help or input will be kindly appreciated !

jQuery counting spaces

Gretings, I have a bit complicated issue. I'm appending text to textarea with javascript. My question is - how to count, how many spaces are on the text area location, where the mouse is clicked? I'm appending text like this:
function typeInTextarea(el, newText) {
var start = el.prop("selectionStart")
var end = el.prop("selectionEnd")
var text = el.val()
var before = text.substring(0, start)
var after = text.substring(end, text.length)
el.val(before + newText + after)
el[0].selectionStart = el[0].selectionEnd = start + newText.length
el.focus()
}
$(".containeris").on("click", function() {
typeInTextarea($("#html_content"), '<div class="container">\n\n</div>')
return false
})
So the problem is, it starts appending text exactly, where mouse is clicked, but i want to make sure, that closing div, which is being printed in new line, would have same number of spaces in front of it as starting line, to keep indentation. Is it possible to achieve?
Solution was simpy to count tabulations as symbols.

How Do I Add Ellipses to Text

I am trying to create a function that will take an element's text, cut off any characters beyond 80, and add an ellipses if necessary. Here's my code so far:
var maxLength = 80;
function shorten(element) {
var text = $('#' + element).text();
var ret = text;
if (text.length > maxLength) {
text = text.substr(0,maxLength-3) + "...";
}
$('#' + element).text(text);
}
shorten('slide1');
So, the function should take the element, remove extra text off the end, add an ellipses, and then replace the old text in the element with the new string I've just created.
When I run the function, I don't get any errors, but it doesn't actually cut off the text as it should. Why is this?
var text = "Some Text Goes Here. La La La La!";
var textLength = 10; // Number of characters to cut off after
function shorten(text, textLength){
if(text.length > textLength){
text = text.substring(0, textLength) + '…';
}
return text;
}
var shortText = shorten(text, textLength);
Also, using the HTML character for ellipsis is better than using three periods.
I've added a Codepen showing the code working. Additionally, I added a function spaceShorten that will split your text at the last occurrence of a space that is less than the length provided, so you don't split the text mid word.
http://codepen.io/supah_frank/pen/EaYzNz

Add tags around selected text in an element

How can I add <span> tags around selected text within an element?
For example, if somebody highlights "John", I would like to add span tags around it.
HTML
<p>My name is Jimmy John, and I hate sandwiches. My name is still Jimmy John.</p>
JS
function getSelectedText() {
t = (document.all) ? document.selection.createRange().text : document.getSelection();
return t;
}
$('p').mouseup(function(){
var selection = getSelectedText();
var selection_text = selection.toString();
console.log(selection);
console.log(selection_text);
// How do I add a span around the selected text?
});
http://jsfiddle.net/2w35p/
There is a identical question here: jQuery select text and add span to it in an paragraph, but it uses outdated jquery methods (e.g. live), and the accepted answer has a bug.
I have a solution. Get the Range of the selecion and deleteContent of it, then insert a new span in it .
$('body').mouseup(function(){
var selection = getSelectedText();
var selection_text = selection.toString();
// How do I add a span around the selected text?
var span = document.createElement('SPAN');
span.textContent = selection_text;
var range = selection.getRangeAt(0);
range.deleteContents();
range.insertNode(span);
});
You can see the DEMO here
UPDATE
Absolutly, the selection will be delete at the same time. So you can add the selection range with js code if you want.
You can simply do like this.
$('body').mouseup(function(){
var span = document.createElement("span");
if (window.getSelection) {
var sel = window.getSelection();
if (sel.rangeCount) {
var range = sel.getRangeAt(0).cloneRange();
range.surroundContents(span);
sel.removeAllRanges();
sel.addRange(range);
}
}
});
Fiddle
Reference Wrapping a selected text node with span
You can try this:
$('body').mouseup(function(){
var selection = getSelectedText();
var innerHTML = $('p').html();
var selectionWithSpan = '<span>'+selection+'</span>';
innerHTML = innerHTML.replace(selection,selectionWithSpan);
$('p').html(innerHTML);
});
and In your fiddle you are again opening a new <p> instead of a closing </p>. Update that please.
THIS WORKS (mostly*)!! (technically, it does what you want, but it needs HALP!)
JSFiddle
This adds <span ...> and </span> correctly, even if there are multiple instances of the selection in your element and you only care about the instance that's selected!
It works perfectly the first time if you include my commented line. It's after that when things get funky.
I can add the span tags, but I'm having a hard time replacing the plaintext with html. Maybe you can figure it out? We're almost there!! This uses nodes from getSelection. Nodes can be hard to work with though.
document.getElementById('d').addEventListener('mouseup',function(e){
var s = window.getSelection();
var n = s.anchorNode; //DOM node
var o = s.anchorOffset; //index of start selection in the node
var f = s.focusOffset; //index of end selection in the node
n.textContent = n.textContent.substring(0,o)+'<span style="color:red;">'
+n.textContent.substring(o,f)+'</span>'
+n.textContent.substring(f,n.textContent.length);
//adds the span tag
// document.getElementById('d').innerHTML = n.textContent;
// this line messes stuff up because of the difference
// between a node's textContent and it's innerHTML.
});

Shuffle selected text on <textarea> using JavaScript

When I select some texts on the <textarea> using my mouse, how can I shuffle/scramble it by clicking on a button?
I've searched for something similar to what I want here on SO, and I saw some who use substring, selectionStart, and selectionEnd.
What I want is: when I select some texts with my mouse, it will be shuffled/scrambled when I click on a button, and the rest of the texts on the <textarea> that are not selected should remain untouched/intact.
I just want to perform an action on the selected texts.
It's more similar to a rich text editor like when you select on some texts, then click on bold button, the selected texts will become bold.
P.S.
It should be shuffled by individual characters.
EDIT:
Got it! I just needed to separate the selection string. My code works now. This is very helpful - https://stackoverflow.com/a/9605191/1101391
Unfortunately, IE 9 and below does not support selectionStart and selectionEnd properties on <input> and <textarea>. Here's the solution that worked for me - https://stackoverflow.com/a/9276457/1101391
You have access to the full text and know the substring where the selection starts and ends. Try something like this:
var txtArea = document.getElementById("foo");
var before = txtArea.value.substr(0, txtArea.selectionStart);
var selection = txtArea.value.substr(txtArea.selectionStart, txtArea.selectionEnd + 1);
var after = txtArea.value.substr(txtArea.selectionEnd, txtArea.value.length);
txtArea.value = before + scrambleThisString(selection) + after;
Suppose you name the textarea with ID content:
var textarea = document.getElementById('content');
var content = textarea.value;
var start = textarea.selectionStart;
var end = textarea.selectionEnd;
var before = content.slice(0, start);
var after = content.slice(end);
var selected = content.substring(start, end);
selected = shuffleStringByMagic(selected);
textarea.value = before + selected + after;

Categories