Wrap with span if text is selected - javascript

I am using the following function to wrap highlighted text with a <span> when the backspace/delete key is hit.
$(document).keydown(function(event) {
var selection = document.getSelection();
typoNumber++;
if (event.keyCode == 8 || event.keyCode == 46) {
event.preventDefault();
var span = document.createElement("span");
span.setAttribute('id', 'typo' + typoNumber);
span.className = "deleted typo";
span.setAttribute('contenteditable', 'false');
if (window.getSelection) {
var sel = window.getSelection();
if (sel.rangeCount) {
var range = sel.getRangeAt(0).cloneRange();
range.surroundContents(span);
sel.removeAllRanges();
sel.addRange(range);
}
}
}
});
.deleted.typo {
background: rgba(100,100,100,0.25);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div>Here is text to experiment with.</div>
I only want the function to run if there is text selected (so that I don't get a bunch of empty <span> elements). I tried changing the first if statement to the following:
if (selection !== '' && event.keyCode == 8 || event.keyCode == 46) {
but that didn't work. Any ideas?

Changing your condition to this should help you:
if(
selection.anchorOffset !== selection.focusOffset
&&
(event.keyCode == 8 || event.keyCode == 46)
)
Fiddle: https://jsfiddle.net/34L49xLr/

getSelection actually returns an object (a SelectionObject) - you can convert that to a string and then check its length to determine whether or not there's any text selected. Something like this:
// Make sure you're initializing this somewhere; this function expects it defined.
var typoNumber = 0;
$(document).keydown(function(event) {
var selection = document.getSelection();
// If there's nothing selected, bail out here.
// If you want to increment typoNumber even with an empty selection,
// move that line above this block.
if (selection.toString().length < 1) {
return;
}
typoNumber++;
if (event.keyCode == 8 || event.keyCode == 46) {
event.preventDefault();
var span = document.createElement("span");
span.setAttribute('id', 'typo' + typoNumber);
span.className = "deleted typo";
span.setAttribute('contenteditable', 'false');
if (window.getSelection) {
var sel = window.getSelection();
if (sel.rangeCount) {
var range = sel.getRangeAt(0).cloneRange();
range.surroundContents(span);
sel.removeAllRanges();
sel.addRange(range);
}
}
}
});
/* so we can see where the spans are... */
.deleted.typo {
display: inline-block;
padding: 3px;
background: rgba(100,100,100,0.25);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div>Here is text to experiment with.</div>

Related

Auto selection (addRange) method not works properly while type the text too fast

While type the text in 4th row very fast ("Apple") the auto selection not works properly as expected. But while editing the single text it works as expected(selection works). Please check the below html sample
https://jsplayground.syncfusion.com/3ruatvy0
HTML5 Editable Table
Apple
Orange
Mango
</table>
<script>
var data = [];
$("table tr").each(function(i, v){
$(this).children('td').each(function(ii, vv){
data.push($(this).text());
});
})
console.log(data);
document.getElementById("table").addEventListener("keyup", event => {
for (var i = data.length -1 ; i > 0; i--) {
var cell = data[i - 1];
if (
event.key == "Backspace" ||
event.key == "Delete" ||
event.key == "Escape"
) {
return;
} else if (cell) {
var elem = document.getElementById("editable");
var textLen = elem.innerHTML.length;
if (
elem.innerHTML.toUpperCase() ==
cell.toUpperCase().slice(0, textLen)
) {
elem.innerText = cell;
var startNode = document.getElementById(
"editable"
).firstChild;
startNode.nodeValue = startNode.nodeValue.trim();
var range = document.createRange();
range.setStart(startNode, textLen);
range.setEnd(startNode, startNode.textContent.length);
var sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);
return;
}
} else return;
}
});
</script>

Creating hashtags in contenteditable with keypress

Edit: code below only works in Chrome/Opera
I've modified the code found here to create hashtags in a contenteditable using the keypress event. Basically, whenever someone types the hash (#) symbol, a span tag (styled via Bootstrap and other css) is immediately created and closes whenever a space or any punctuation character is pressed. That's the long and short of it.
Here's my code:
function placeCaretAtEnd(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();
}
}
// closes the span housing the hashtag
function closeSpan(){
var span = document.createElement("span");
span.setAttribute("data-role", "remove");
span.setAttribute("class", "delHashtag");
span.setAttribute("aria-hidden", "true");
return span;
}
$(document).ready(function() {
var hashtags = false;
$(document).on('keypress', '#myInputField', function(e) {
var input_field = $(this);
var x = e.which || e.keyCode;
if (x == 32){ // space key
if(hashtags){
e.preventDefault();
input_field.html(input_field.html() + "</span> ");
placeCaretAtEnd(this);
hashtags = false;
document.getElementsByClassName("new")[0].setAttribute("contenteditable", false);
document.getElementsByClassName("new")[0].appendChild(closeSpan());
}
}
if (x == 35){ // hash key (#)
e.preventDefault();
$(".tag").removeClass("new");
input_field.html(input_field.html() + "<span class='tag label label-info new'>#");
placeCaretAtEnd(this);
hashtags = true;
}
// various punctuation characters
if (x == 8 || x == 9 || x >=16 && x <= 18 || x == 27 || x == 33 || x == 34 || x >= 36 && x <= 47 || x >= 58 && x <= 64 || x >= 91 && x <= 94 || x == 96 || x >= 123 && x <= 126) {
if(hashtags) {
e.preventDefault();
input_field.html(input_field.html() + "</span>" + String.fromCharCode(x));
placeCaretAtEnd(this);
hashtags = false;
document.getElementsByClassName("new")[0].setAttribute("contenteditable", false);
document.getElementsByClassName("new")[0].appendChild(closeSpan());
}
}
if(x == 13){// return key
document.execCommand('defaultParagraphSeparator', false, 'p');
}
});
$(document).on("click", ".delHashtag", function() {
this.parentNode.parentNode.removeChild(this.parentNode);
return false;
});
});
#myInputField {
border:1px solid #ddd;
padding: 10px;
font-size: 14px;
}
.tag span[data-role="remove"] {
margin-left: 8px;
cursor: pointer;
}
.tag span[data-role="remove"]:after {
content: "x";
padding: 0px 2px;
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<div id="myInputField" contenteditable="true"></div>
Hashtags are created as expected. However, should you click somewhere else in the text and try to create a hashtag, it isn't created where the cursor is. Instead, the hashtag goes to the end of the contenteditable. Not good.
I haven't been able to figure this out. Sure, there are other problems with the code (i.e. no validation of hashtags; first hashtag created after pressing return appears on another line (doesn't remain inside p/div tag)), but I'm focused on this one issue for now.
Could a Javascript ninja give me a hand? I'd really appreciate it.

jquery getting the html of highlighted text and changing its style

Saw this post about getting the html from the selected/higlighted text. And it does work in getting the html.
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;
}
alert(getSelectionHtml());
But, suppose there's this html:
<p>I am <span style="color:green;">green</span> and I have <span style="background-color:yellow;">yellow background<span>.</p>
Now if the word green is highlighted, then it will just get the word
"green"
even if there's <span> tag surrounded. But if selected more character than the word green (say the space before or after the word green like this " green"), it will get the html tag too, such as:
<span style="color:green;">green</span>
Please check out the demo
Is it possible to get the html even if only the word "green" is highlighted?
Eventually what I want to achieve is suppose I want to change the color of the highlighted text to blue color, then first check if the highlighted text has a span. Secondly whether that span has color or background or even both styling. And lastly do the changes to the highlighted text.
$('#change_blue').on("click", function() {
var sel = getSelectionHtml();
/*var span = sel.find("<span").html();*/
alert(sel);
if (sel.match("<span style=")) {
console.log('has span tag');
if (sel.indexOf("background") > -1 && sel.indexOf("color") > -1 ) {
console.log('has both background and color')
// change color to blue
}
else if (sel.match("color")) {
console.log('only color')
// change color to blue
}
else {
console.log('only background')
// add blue color
}
}
else {
console.log('no span tag');
}
});
How can I get the html of a highlighted text and change it accordingly? It would really mean a lot if you could help me through. Thank you.
Demo
Do this,
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, range; i < len; ++i) {
range = sel.getRangeAt(i);
if (range.startContainer === range.endContainer
&& range.startContainer.nodeType === Node.TEXT_NODE
&& range.startOffset === 0
&& range.endOffset === range.startContainer.length) {
range.selectNode(range.startContainer.parentElement);
}
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;
}

HTML text area tab support

I am making a web based code editor and am using a textarea for text editing. I want to add tab support to the textarea so that pressing tab doesn't de-focus the element.
I have the textarea defined like this:
<textarea id="codeEdit_txt" rows="50" cols="80" onkeydown="return codeEdit_keyDown(event);">
and the function codeEdit_keyDown defined as:
function codeEdit_keyDown(e) {
if (e.keyCode == 9) {
return false;
}
}
This prevents the tab key press from de-focusing the textarea, though it doesn't leave the tab character behind. While I was trying to get this to work initially, I noticed that if I defined the function as below, it would put a tab character at the cursor position.
function codeEdit_keyDown(e) {
if (e.keyCode == 9) {
alert("");
return false;
}
}
My two questions are:
Why does adding the alert cause a tab to be added?
Is there a way to add the tab at the cursor without having to find the cursor
position, split the text in the texarea and manually add a tab
character (and without having to have an alert every time the user pressed tab)?
Thanks
EDIT: This only seems to work in Chrome, not in IE, Safari or Firefox
See this question:
https://stackoverflow.com/a/13130/420001
You're looking for .preventDefault();
EDIT: A fiddle.
EDIT 2: A better fiddle, thanks to rainecc.
The other answer is nice, but it ends tabs at the end.
I looked up how to add the tab at the cursor location, and added that to the solution.
You can find the working code here: http://jsfiddle.net/felixc/o2ptfd5z/9/
Code inline as a safeguard:
function insertAtCursor(myField, myValue) {
//IE support
if (document.selection) {
myField.focus();
sel = document.selection.createRange();
sel.text = myValue;
}
//MOZILLA and others
else if (myField.selectionStart || myField.selectionStart == '0') {
var startPos = myField.selectionStart;
var endPos = myField.selectionEnd;
myField.value = myField.value.substring(0, startPos)
+ myValue
+ myField.value.substring(endPos, myField.value.length);
myField.selectionStart = startPos + myValue.length;
myField.selectionEnd = startPos + myValue.length;
} else {
myField.value += myValue;
}
}
function addTabSupport(elementID, tabString) {
// Get textarea element
var myInput = document.getElementById(elementID);
// At keydown: Add tab character at cursor location
function keyHandler(e) {
var TABKEY = 9;
if(e.keyCode == TABKEY) {
insertAtCursor(myInput, tabString);
if(e.preventDefault) {
e.preventDefault();
}
return false;
}
}
// Add keydown listener
if(myInput.addEventListener ) {
myInput.addEventListener('keydown',keyHandler,false);
} else if(myInput.attachEvent ) {
myInput.attachEvent('onkeydown',this.keyHandler); /* damn IE hack */
}
}
// easily add tab support to any textarea you like
addTabSupport("input", "\t");
<h1>Click in the text and hit tab</h1>
<textarea id="input" rows=10 cols=50>function custom(data){
return data;
}</textarea>
Here's what I used for my own editor (using some other answers) :
function insertAtCursor (el, text) {
text = text || '';
if (document.selection) {
// IE
el.focus();
var sel = document.selection.createRange();
sel.text = text;
} else if (el.selectionStart || el.selectionStart === 0) {
// Others
var startPos = el.selectionStart;
var endPos = el.selectionEnd;
el.value = el.value.substring(0, startPos) +
text +
el.value.substring(endPos, el.value.length);
el.selectionStart = startPos + text.length;
el.selectionEnd = startPos + text.length;
} else {
el.value += text;
}
};
document.querySelector("#editor").addEventListener("keydown", function(e) {
var TABKEY = 9;
if(e.keyCode == TABKEY) {
insertAtCursor(this, "\t");
if(e.preventDefault) {
e.preventDefault();
}
return false;
}
}, false);

Get parent element of a selected text

Is it possible to get the parent element of a selected text in the page? For example:
<div class="someparent">
Selection of this text should refer to the 'someparent' class.
<span class="spanparent">If this is selected, the parent should be this span</span>
</div>
Because when getting the selected text, it normally gets it from the window or the document (depending on the browser) but is it that possible to get the parent element of the selected text?
Here's a function that will get you the innermost element that contains the whole of the user selection in all major browsers (except when multiple ranges are selected, which is only supported in Firefox. If this is important, I can expand the example to deal with that case too):
function getSelectionParentElement() {
var parentEl = null, sel;
if (window.getSelection) {
sel = window.getSelection();
if (sel.rangeCount) {
parentEl = sel.getRangeAt(0).commonAncestorContainer;
if (parentEl.nodeType != 1) {
parentEl = parentEl.parentNode;
}
}
} else if ( (sel = document.selection) && sel.type != "Control") {
parentEl = sel.createRange().parentElement();
}
return parentEl;
}
I'd suggest to use this
window.getSelection().anchorNode.parentElement
I have tested in safari osx 10.9
#Tim Down's answer works good, to add more useful code for reaching the specific parent's html content:
function getSelectionParentElement() {
var parentEl = null, sel;
if (window.getSelection) {
sel = window.getSelection();
if (sel.rangeCount) {
parentEl = sel.getRangeAt(0).commonAncestorContainer;
if (parentEl.nodeType != 1) {
parentEl = parentEl.parentNode;
}
}
} else if ( (sel = document.selection) && sel.type != "Control") {
parentEl = sel.createRange().parentElement();
}
while(true){
// I want to reach upper <span> parent
if(parentEl.nodeName == "SPAN"){
console.log(parentEl);
break;
}
else{
parentEl = parentEl.parentNode;
}
}
}
For example:
function getSelectionParentElement() {
var parentEl = null, sel;
if (window.getSelection) {
sel = window.getSelection();
if (sel.rangeCount) {
parentEl = sel.getRangeAt(0).commonAncestorContainer;
if (parentEl.nodeType != 1) {
parentEl = parentEl.parentNode;
}
}
} else if ( (sel = document.selection) && sel.type != "Control") {
parentEl = sel.createRange().parentElement();
}
while(true){
// I want to reach upper <span> parent
if(parentEl.nodeName == "P"){
document.getElementById("printable").innerText = parentEl.innerHTML;
break;
}
else{
parentEl = parentEl.parentNode;
}
}
}
<head>
<style type="text/css">
#media print
{
#non-printable { display: none; }
#printable { display: block; }
}
</style>
</head>
<p>The <strong>coronavirus</strong> COVID-19 is affecting <strong>210 <i>countries</i> and territories</strong> around the world and 2 international conveyances.</p>
<div id="printable">Output: </div>
<button onclick="getSelectionParentElement()">Select 'countries' and click me.</button>

Categories