Prefix/Suffix highlighted text in textbox - javascript

I have to make a small javascript function that adds a prefix and suffix to a selected text within a textbox.
This is what I have so far:
function AddTags(name, prefix, suffix) {
try
{
var textArea = document.getElementById(name).value;
var i = 0;
var textArray = textArea.split("\n");
if (textArray == null) {
document.getElementById(name).value += prefix + suffix
}
else {
for (i = 0; i < textArray.length; i++) {
textArray[i] = prefix + textArray[i] + suffix;
}
document.getElementById(name).value = textArray.join("\n");
}
}
catch (err) { }
}
Now this function adds the provided prefix and suffix to every line, but I need to find out how to break up my textbox's text in Text before selection, Selected text and Text after selection.
Anybody any experience on this?
EDIT:
TriniBoy's function set me on the right track. I didn't need the whole suggestion.
This is the edited version of my original code:
function AddTags(name, prefix, suffix) {
try
{
var textArea = document.getElementById(name);
var i = 0;
var selStart = textArea.selectionStart;
var selEnd = textArea.selectionEnd;
var textbefore = textArea.value.substring(0, selStart);
var selected = textArea.value.substring(selStart, selEnd);
var textAfter = textArea.value.substring(selEnd);
if (textAfter == "") {
document.getElementById(name).value += prefix + suffix
}
else {
document.getElementById(name).value = textbefore + prefix + selected + suffix + textAfter;
}
}
catch (err) { }
}
Thx TriniBoy, I'll mark your leg-up as answer.

Based on your demo and your explanation, hopefully I got your requirements correct.
See code comments for a break down.
See demo fiddle here
var PreSuffApp = PreSuffApp || {
selText: "",
selStart: 0,
selEnd: 0,
getSelectedText: function (id) {
var text = "",
docSel = document.selection, //For IE
winSel = window.getSelection,
P = PreSuffApp,
textArea = document.getElementById(id);
if (typeof winSel !== "undefined") {
text = winSel().toString(); //Grab the current selected text
if (typeof docSel !== "undefined" && docSel.type === "Text") {
text = docSel.createRange().text; //Grab the current selected text
}
}
P.selStart = textArea.selectionStart; //Get the start of the selection range
P.selEnd = textArea.selectionEnd; //Get the end of the selection range
P.selText = text; //Set the value of the current selected text
},
addTags: function (id, prefix, suffix) {
try {
var textArea = document.getElementById(id),
P = PreSuffApp,
range = P.selEnd - P.selStart; //Used to calculate the lenght of the selection
//Check to see if some valuable text is selected
if (P.selText.trim() !== "") {
textArea.value = textArea.value.splice(P.selStart, range, prefix + P.selText + suffix); //Call the splice method on your text area value
} else {
alert("You've selected a bunch of nothingness");
}
} catch (err) {}
}
};
//Extend the string obj to splice the string from a start character index to an end range, like an array.
String.prototype.splice = function (index, rem, s) {
return (this.slice(0, index) + s + this.slice(index + Math.abs(rem)));
};

Related

How to get a text character from clicked screen position coordinates where text contains ruby/other tags Javascript

I am aware of the method where spans are added to each character and each span has an onClick listener. However, this does not work with text with tags such as ruby or bold e.g.
<p>
<ruby>漆黒<rt>しっこく</rt></ruby>の<ruby>闇<rt>やみ</rt></ruby>だった。
</p>
my current method below turns the above p tag into
<p>漆黒しっこくの闇やみだった。</p>
(it removes the tags inside the p element)
document.querySelectorAll('p').forEach(el => {
let elContent = el['innerText'];
let characters = el['innerText'].split('');
el.innerHTML = '';
characters.forEach(char => {
let span = document.createElement('span');
span.innerText = char;
span.addEventListener('click', function () {
let position = 0;
let el = this;
while (el.previousSibling !== null) {
position++;
el = el.previousSibling;
}
console.log(elContent + ' : ' + this.innerHTML + ' : ' + position);
});
el.appendChild(span);
});
});
is there a way to get some character of text from just screen coordinates like
function click(event) {
console.log("x:" + event.clientX + "y:" + event.clientY)
// how can I get the character of text at these coordinates(assuming I did infact click a letter)
}
document.addEventListener("click", click);
Edit:
I understand this can also be done with ranges but this is not the purpose of range and is pretty inaccurate.
document.onclick = (evt) => {
const sel = getSelection();
if (sel.rangeCount) {
const range = sel.getRangeAt(0);
const targetedNode = range.startContainer;
const clickedLetter = targetedNode.textContent.substr(range.startOffset, 1);
console.log(clickedLetter);
}
};
ok I have a solution, I can just loop through the body and add the spans on any character not in a tag
function addSpans() {
html = document.body.innerHTML
newHtml = ""
intag = false
tags = []
// add the spans to the relevant characters
for (var i = 0; i < html.length; i++) {
if (!intag) {
if (html[i] !== "<" && html[i] !== "\n") {
newHtml += '<span id="textTag' + tags.length + '">' + html[i] + '</span>'
tags.push(html[i])
} else {
intag = true
}
}
if (intag) {
newHtml += html[i]
if (html[i] === ">") {
intag = false
}
}
}
// return newHtml
document.body.innerHTML = newHtml
// add the event listners
for (var i = 0; i < tags.length; i++) {
document.getElementById("textTag"+i).addEventListener('click', function () {
var id = this.id.replace("textTag", "")
console.log(tags[id])
})
}
}
addSpans()

How to get the highest level parent of clicked element [duplicate]

HTML
<body>
<div class="lol">
<a class="rightArrow" href="javascriptVoid:(0);" title"Next image">
</div>
</body>
Pseudo Code
$(".rightArrow").click(function() {
rightArrowParents = this.dom(); //.dom(); is the pseudo function ... it should show the whole
alert(rightArrowParents);
});
Alert message would be:
body div.lol a.rightArrow
How can I get this with javascript/jquery?
Here is a native JS version that returns a jQuery path. I'm also adding IDs for elements if they have them. This would give you the opportunity to do the shortest path if you see an id in the array.
var path = getDomPath(element);
console.log(path.join(' > '));
Outputs
body > section:eq(0) > div:eq(3) > section#content > section#firehose > div#firehoselist > article#firehose-46813651 > header > h2 > span#title-46813651
Here is the function.
function getDomPath(el) {
var stack = [];
while ( el.parentNode != null ) {
console.log(el.nodeName);
var sibCount = 0;
var sibIndex = 0;
for ( var i = 0; i < el.parentNode.childNodes.length; i++ ) {
var sib = el.parentNode.childNodes[i];
if ( sib.nodeName == el.nodeName ) {
if ( sib === el ) {
sibIndex = sibCount;
}
sibCount++;
}
}
if ( el.hasAttribute('id') && el.id != '' ) {
stack.unshift(el.nodeName.toLowerCase() + '#' + el.id);
} else if ( sibCount > 1 ) {
stack.unshift(el.nodeName.toLowerCase() + ':eq(' + sibIndex + ')');
} else {
stack.unshift(el.nodeName.toLowerCase());
}
el = el.parentNode;
}
return stack.slice(1); // removes the html element
}
Using jQuery, like this (followed by a solution that doesn't use jQuery except for the event; lots fewer function calls, if that's important):
$(".rightArrow").click(function () {
const rightArrowParents = [];
$(this)
.parents()
.addBack()
.not("html")
.each(function () {
let entry = this.tagName.toLowerCase();
const className = this.className.trim();
if (className) {
entry += "." + className.replace(/ +/g, ".");
}
rightArrowParents.push(entry);
});
console.log(rightArrowParents.join(" "));
return false;
});
Live example:
$(".rightArrow").click(function () {
const rightArrowParents = [];
$(this)
.parents()
.addBack()
.not("html")
.each(function () {
let entry = this.tagName.toLowerCase();
const className = this.className.trim();
if (className) {
entry += "." + className.replace(/ +/g, ".");
}
rightArrowParents.push(entry);
});
console.log(rightArrowParents.join(" "));
return false;
});
<div class=" lol multi ">
Click here
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
(In the live examples, I've updated the class attribute on the div to be lol multi to demonstrate handling multiple classes.)
That uses parents to get the ancestors of the element that was clicked, removes the html element from that via not (since you started at body), then loops through creating entries for each parent and pushing them on an array. Then we use addBack to add the a back into the set, which also changes the order of the set to what you wanted (parents is special, it gives you the parents in the reverse of the order you wanted, but then addBack puts it back in DOM order). Then it uses Array#join to create the space-delimited string.
When creating the entry, we trim className (since leading and trailing spaces are preserved, but meaningless, in the class attribute), and then if there's anything left we replace any series of one or more spaces with a . to support elements that have more than one class (<p class='foo bar'> has className = "foo bar", so that entry ends up being p.foo.bar).
Just for completeness, this is one of those places where jQuery may be overkill, you can readily do this just by walking up the DOM:
$(".rightArrow").click(function () {
const rightArrowParents = [];
for (let elm = this; elm; elm = elm.parentNode) {
let entry = elm.tagName.toLowerCase();
if (entry === "html") {
break;
}
const className = elm.className.trim();
if (className) {
entry += "." + className.replace(/ +/g, ".");
}
rightArrowParents.push(entry);
}
rightArrowParents.reverse();
console.log(rightArrowParents.join(" "));
return false;
});
Live example:
$(".rightArrow").click(function () {
const rightArrowParents = [];
for (let elm = this; elm; elm = elm.parentNode) {
let entry = elm.tagName.toLowerCase();
if (entry === "html") {
break;
}
const className = elm.className.trim();
if (className) {
entry += "." + className.replace(/ +/g, ".");
}
rightArrowParents.push(entry);
}
rightArrowParents.reverse();
console.log(rightArrowParents.join(" "));
return false;
});
<div class=" lol multi ">
Click here
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
There we just use the standard parentNode property (or we could use parentElement) of the element repeatedly to walk up the tree until either we run out of parents or we see the html element. Then we reverse our array (since it's backward to the output you wanted), and join it, and we're good to go.
I needed a native JS version, that returns CSS standard path (not jQuery), and deals with ShadowDOM. This code is a minor update on Michael Connor's answer, just in case someone else needs it:
function getDomPath(el) {
if (!el) {
return;
}
var stack = [];
var isShadow = false;
while (el.parentNode != null) {
// console.log(el.nodeName);
var sibCount = 0;
var sibIndex = 0;
// get sibling indexes
for ( var i = 0; i < el.parentNode.childNodes.length; i++ ) {
var sib = el.parentNode.childNodes[i];
if ( sib.nodeName == el.nodeName ) {
if ( sib === el ) {
sibIndex = sibCount;
}
sibCount++;
}
}
// if ( el.hasAttribute('id') && el.id != '' ) { no id shortcuts, ids are not unique in shadowDom
// stack.unshift(el.nodeName.toLowerCase() + '#' + el.id);
// } else
var nodeName = el.nodeName.toLowerCase();
if (isShadow) {
nodeName += "::shadow";
isShadow = false;
}
if ( sibCount > 1 ) {
stack.unshift(nodeName + ':nth-of-type(' + (sibIndex + 1) + ')');
} else {
stack.unshift(nodeName);
}
el = el.parentNode;
if (el.nodeType === 11) { // for shadow dom, we
isShadow = true;
el = el.host;
}
}
stack.splice(0,1); // removes the html element
return stack.join(' > ');
}
Here is a solution for exact matching of an element.
It is important to understand that the selector (it is not a real one) that the chrome tools show do not uniquely identify an element in the DOM. (for example it will not distinguish between a list of consecutive span elements. there is no positioning/indexing info)
An adaptation from a similar (about xpath) answer
$.fn.fullSelector = function () {
var path = this.parents().addBack();
var quickCss = path.get().map(function (item) {
var self = $(item),
id = item.id ? '#' + item.id : '',
clss = item.classList.length ? item.classList.toString().split(' ').map(function (c) {
return '.' + c;
}).join('') : '',
name = item.nodeName.toLowerCase(),
index = self.siblings(name).length ? ':nth-child(' + (self.index() + 1) + ')' : '';
if (name === 'html' || name === 'body') {
return name;
}
return name + index + id + clss;
}).join(' > ');
return quickCss;
};
And you can use it like this
console.log( $('some-selector').fullSelector() );
Demo at http://jsfiddle.net/gaby/zhnr198y/
The short vanilla ES6 version I ended up using:
Returns the output I'm used to read in Chrome inspector e.g body div.container input#name
function getDomPath(el) {
let nodeName = el.nodeName.toLowerCase();
if (el === document.body) return 'body';
if (el.id) nodeName += '#' + el.id;
else if (el.classList.length)
nodeName += '.' + [...el.classList].join('.');
return getDomPath(el.parentNode) + ' ' + nodeName;
};
I moved the snippet from T.J. Crowder to a tiny jQuery Plugin. I used the jQuery version of him even if he's right that this is totally unnecessary overhead, but i only use it for debugging purpose so i don't care.
Usage:
Html
<html>
<body>
<!-- Two spans, the first will be chosen -->
<div>
<span>Nested span</span>
</div>
<span>Simple span</span>
<!-- Pre element -->
<pre>Pre</pre>
</body>
</html>
Javascript
// result (array): ["body", "div.sampleClass"]
$('span').getDomPath(false)
// result (string): body > div.sampleClass
$('span').getDomPath()
// result (array): ["body", "div#test"]
$('pre').getDomPath(false)
// result (string): body > div#test
$('pre').getDomPath()
Repository
https://bitbucket.org/tehrengruber/jquery.dom.path
I've been using Michael Connor's answer and made a few improvements to it.
Using ES6 syntax
Using nth-of-type instead of nth-child, since nth-of-type looks for children of the same type, rather than any child
Removing the html node in a cleaner way
Ignoring the nodeName of elements with an id
Only showing the path until the closest id, if any. This should make the code a bit more resilient, but I left a comment on which line to remove if you don't want this behavior
Use CSS.escape to handle special characters in IDs and node names
~
export default function getDomPath(el) {
const stack = []
while (el.parentNode !== null) {
let sibCount = 0
let sibIndex = 0
for (let i = 0; i < el.parentNode.childNodes.length; i += 1) {
const sib = el.parentNode.childNodes[i]
if (sib.nodeName === el.nodeName) {
if (sib === el) {
sibIndex = sibCount
break
}
sibCount += 1
}
}
const nodeName = CSS.escape(el.nodeName.toLowerCase())
// Ignore `html` as a parent node
if (nodeName === 'html') break
if (el.hasAttribute('id') && el.id !== '') {
stack.unshift(`#${CSS.escape(el.id)}`)
// Remove this `break` if you want the entire path
break
} else if (sibIndex > 0) {
// :nth-of-type is 1-indexed
stack.unshift(`${nodeName}:nth-of-type(${sibIndex + 1})`)
} else {
stack.unshift(nodeName)
}
el = el.parentNode
}
return stack
}
All the examples from other ответов did not work very correctly for me, I made my own, maybe my version will be more suitable for the rest
const getDomPath = element => {
let templateElement = element
, stack = []
for (;;) {
if (!!templateElement) {
let attrs = ''
for (let i = 0; i < templateElement.attributes.length; i++) {
const name = templateElement.attributes[i].name
if (name === 'class' || name === 'id') {
attrs += `[${name}="${templateElement.getAttribute(name)}"]`
}
}
stack.push(templateElement.tagName.toLowerCase() + attrs)
templateElement = templateElement.parentElement
} else {
break
}
}
return stack.reverse().slice(1).join(' > ')
}
const currentElement = document.querySelectorAll('[class="serp-item__thumb justifier__thumb"]')[7]
const path = getDomPath(currentElement)
console.log(path)
console.log(document.querySelector(path))
console.log(currentElement)
var obj = $('#show-editor-button'),
path = '';
while (typeof obj.prop('tagName') != "undefined"){
if (obj.attr('class')){
path = '.'+obj.attr('class').replace(/\s/g , ".") + path;
}
if (obj.attr('id')){
path = '#'+obj.attr('id') + path;
}
path = ' ' +obj.prop('tagName').toLowerCase() + path;
obj = obj.parent();
}
console.log(path);
hello this function solve the bug related to current element not show in the path
check this now
$j(".wrapper").click(function(event) {
selectedElement=$j(event.target);
var rightArrowParents = [];
$j(event.target).parents().not('html,body').each(function() {
var entry = this.tagName.toLowerCase();
if (this.className) {
entry += "." + this.className.replace(/ /g, '.');
}else if(this.id){
entry += "#" + this.id;
}
entry=replaceAll(entry,'..','.');
rightArrowParents.push(entry);
});
rightArrowParents.reverse();
//if(event.target.nodeName.toLowerCase()=="a" || event.target.nodeName.toLowerCase()=="h1"){
var entry = event.target.nodeName.toLowerCase();
if (event.target.className) {
entry += "." + event.target.className.replace(/ /g, '.');
}else if(event.target.id){
entry += "#" + event.target.id;
}
rightArrowParents.push(entry);
// }
where $j = jQuery Variable
also solve the issue with .. in class name
here is replace function :
function escapeRegExp(str) {
return str.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, "\\$1");
}
function replaceAll(str, find, replace) {
return str.replace(new RegExp(escapeRegExp(find), 'g'), replace);
}
Thanks
$(".rightArrow")
.parents()
.map(function () {
var value = this.tagName.toLowerCase();
if (this.className) {
value += '.' + this.className.replace(' ', '.', 'g');
}
return value;
})
.get().reverse().join(", ");

How to highlight first occurrence of a string in a UIWebView?

I use the javascript code below to find all occurrences of a string in UIWebView:
UIWebViewSearch.js:
var uiWebview_SearchResultCount = 0;
/*!
#method uiWebview_HighlightAllOccurencesOfStringForElement
#abstract // helper function, recursively searches in elements and their child nodes
#discussion // helper function, recursively searches in elements and their child nodes
element - HTML elements
keyword - string to search
*/
function uiWebview_HighlightAllOccurencesOfStringForElement(element,keyword) {
if (element) {
if (element.nodeType == 3) { // Text node
var count = 0;
var elementTmp = element;
while (true) {
var value = elementTmp.nodeValue; // Search for keyword in text node
var idx = value.toLowerCase().indexOf(keyword);
if (idx < 0) break;
count++;
elementTmp = document.createTextNode(value.substr(idx+keyword.length));
}
uiWebview_SearchResultCount += count;
var index = uiWebview_SearchResultCount;
while (true) {
var value = element.nodeValue; // Search for keyword in text node
var idx = value.toLowerCase().indexOf(keyword);
if (idx < 0) break; // not found, abort
//we create a SPAN element for every parts of matched keywords
var span = document.createElement("span");
var text = document.createTextNode(value.substr(idx,keyword.length));
span.appendChild(text);
span.setAttribute("class","uiWebviewHighlight");
span.style.backgroundColor="yellow";
span.style.color="black";
index--;
span.setAttribute("id", "SEARCH WORD"+(index));
//span.setAttribute("id", "SEARCH WORD"+uiWebview_SearchResultCount);
//element.parentNode.setAttribute("id", "SEARCH WORD"+uiWebview_SearchResultCount);
//uiWebview_SearchResultCount++; // update the counter
text = document.createTextNode(value.substr(idx+keyword.length));
element.deleteData(idx, value.length - idx);
var next = element.nextSibling;
//alert(element.parentNode);
element.parentNode.insertBefore(span, next);
element.parentNode.insertBefore(text, next);
element = text;
}
} else if (element.nodeType == 1) { // Element node
if (element.style.display != "none" && element.nodeName.toLowerCase() != 'select') {
for (var i=element.childNodes.length-1; i>=0; i--) {
uiWebview_HighlightAllOccurencesOfStringForElement(element.childNodes[i],keyword);
}
}
}
}
}
// the main entry point to start the search
function uiWebview_HighlightAllOccurencesOfString(keyword) {
uiWebview_RemoveAllHighlights();
uiWebview_HighlightAllOccurencesOfStringForElement(document.body, keyword.toLowerCase());
}
// helper function, recursively removes the highlights in elements and their childs
function uiWebview_RemoveAllHighlightsForElement(element) {
if (element) {
if (element.nodeType == 1) {
if (element.getAttribute("class") == "uiWebviewHighlight") {
var text = element.removeChild(element.firstChild);
element.parentNode.insertBefore(text,element);
element.parentNode.removeChild(element);
return true;
} else {
var normalize = false;
for (var i=element.childNodes.length-1; i>=0; i--) {
if (uiWebview_RemoveAllHighlightsForElement(element.childNodes[i])) {
normalize = true;
}
}
if (normalize) {
element.normalize();
}
}
}
}
return false;
}
// the main entry point to remove the highlights
function uiWebview_RemoveAllHighlights() {
uiWebview_SearchResultCount = 0;
uiWebview_RemoveAllHighlightsForElement(document.body);
}
function uiWebview_ScrollTo(idx) {
var scrollTo = document.getElementById("SEARCH WORD" + idx);
if (scrollTo) scrollTo.scrollIntoView();
}
And in my ViewController.swift file, I load the javascript file and perform a search like so:
func highlightAllOccurencesOfString(str:String) -> Int {
let path = Bundle.main.path(forResource: "UIWebViewSearch", ofType: "js")
var jsCode = ""
do{
jsCode = try String(contentsOfFile: path!)
myWebView.stringByEvaluatingJavaScript(from: jsCode)
let startSearch = "uiWebview_HighlightAllOccurencesOfString('\(str)')"
myWebView.stringByEvaluatingJavaScript(from: startSearch)
let result = myWebView.stringByEvaluatingJavaScript(from: "uiWebview_SearchResultCount")!
}catch
{
// do something
}
return Int(result)!
}
This finds and highlights all occurrences of a string within the webview, but I only want the first occurrence highlighted.
For example, with this code when I call:
highlightAllOccurencesOfString(str:"Hello")
All instances of "Hello" gets highlighted in the webview:
Hello Frank, Hello Noah
But I want this result:
Hello Frank, Hello Noah
How do I modify the javascript code to highlight only the first occurrence of a searched string?
UPDATE: I tried JonLuca's answer below but nothing got highlighted.
Any help would be greatly appreciated
The function is operating like a tree, where it checks if the element is a text node, and if not it calls itself on every one of its children.
You have to let the caller know that you found a single element to highlight, and then leave.
Also, the way this searches through it does not guarantee that what you consider is the "first" occurrence of the word will be the first occurence within the document.body tree.
Within the code, have it return true if it found a word to highlight, and false otherwise. That'll prevent it from continuing its search. Modify the js like so:
function uiWebview_HighlightAllOccurencesOfStringForElement(element, keyword) {
if (element) {
if (element.nodeType == 3) { // Text node
var count = 0;
var elementTmp = element;
var value = elementTmp.nodeValue; // Search for keyword in text node
var idx = value.toLowerCase().indexOf(keyword);
if (idx < 0) {
return false;
}
count++;
elementTmp = document.createTextNode(value.substr(idx + keyword.length));
uiWebview_SearchResultCount += count;
var index = uiWebview_SearchResultCount;
var value = element.nodeValue; // Search for keyword in text node
var idx = value.toLowerCase().indexOf(keyword);
if (idx < 0) {
return false;
} // not found, abort
//we create a SPAN element for every parts of matched keywords
var span = document.createElement("span");
var text = document.createTextNode(value.substr(idx, keyword.length));
span.appendChild(text);
span.setAttribute("class", "uiWebviewHighlight");
span.style.backgroundColor = "yellow";
span.style.color = "black";
index--;
span.setAttribute("id", "SEARCH WORD" + (index));
//span.setAttribute("id", "SEARCH WORD"+uiWebview_SearchResultCount);
//element.parentNode.setAttribute("id", "SEARCH WORD"+uiWebview_SearchResultCount);
//uiWebview_SearchResultCount++; // update the counter
text = document.createTextNode(value.substr(idx + keyword.length));
element.deleteData(idx, value.length - idx);
var next = element.nextSibling;
//alert(element.parentNode);
element.parentNode.insertBefore(span, next);
element.parentNode.insertBefore(text, next);
element = text;
return true;
} else if (element.nodeType == 1) { // Element node
if (element.style.display != "none" && element.nodeName.toLowerCase() != 'select') {
for (var i = element.childNodes.length - 1; i >= 0; i--) {
if (uiWebview_HighlightAllOccurencesOfStringForElement(element.childNodes[i], keyword)) {
return true;
}
}
}
}
}
}
This isn't guaranteed to be correct, or that it covers all of your use cases. However, I had it return true on the first occurrence of finding the highlighted word. It should stop executing and not highlight any other words.
Let me know if this works or not - I don't have the ability to test it right now.

Set cursor at end of string in CKEditor

I wrote the code below to set the first letter to upper case and set the data in CKEditor. The first letter is converting fine, but afterward, the cursor focus is set before the first letter. How can I set the cursor after the character?
CKEDITOR.on('instanceCreated', function (e) {
e.editor.on('contentDom', function () {
e.editor.document.on('keyup', function (event) {
var data = CKEDITOR.instances.editor1.getData();
var str = $(data).text();
var n = str.length;
if (str != null && n == 1) {
CKEDITOR.instances['editor1'].setData(titleCase(str))
function titleCase(string) {
return string.charAt(0).toUpperCase() + string.slice(1);
}
}
});
});
});
this code is work for you :
$(document).ready(function() {
CKEDITOR.on('instanceReady', function(ev) {
ev.editor.focus();
var s = ev.editor.getSelection(); // getting selection
var selected_ranges = s.getRanges(); // getting ranges
var node = selected_ranges[0].startContainer; // selecting the starting node
var parents = node.getParents(true);
node = parents[parents.length - 2].getFirst();
while (true) {
var x = node.getNext();
if (x == null) {
break;
}
node = x;
}
s.selectElement(node);
selected_ranges = s.getRanges();
selected_ranges[0].collapse(false); // false collapses the range to the end of the selected node, true before the node.
s.selectRanges(selected_ranges); // putting the current selection there
});
});

How to get neighbor character from selected text?

I have a string like this:
var comment = 'this is a test';
Assume this i is selected, Now I need to null (left side) and s (right side). How can I get them?
I can get selected text like 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; 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;
}
var selected_text = getSelectionHtml();
document.addEventListener("click", function() {
var selection = window.getSelection();
// Check if there are any ranges selected.
if (selection.rangeCount > 0 && selection.type == "Range") {
// Text content of the element.
var text = selection.anchorNode.textContent;
// selection.anchorOffset is the start position of the selection
var before = text.substring(selection.anchorOffset-1, selection.anchorOffset);
// selection.extentOffset is the end position of the selection
var after = text.substring(selection.extentOffset, selection.extentOffset+1);
// Check if there are any letters before or after selected string.
// If not, change before and after to null.
before = before.length === 1 ? before : null;
after = after.length === 1 ? after : null;
console.log(before, after);
}
});
<div>this is a test</div>
To get two characters:
document.addEventListener("click", function() {
var selection = window.getSelection();
// Check if there are any ranges selected.
if (selection.rangeCount > 0 && selection.type == "Range") {
// Text content of the element.
var text = selection.anchorNode.textContent;
// selection.anchorOffset is the start position of the selection
var before = text.substring(selection.anchorOffset-2, selection.anchorOffset);
// selection.extentOffset is the end position of the selection
var after = text.substring(selection.extentOffset, selection.extentOffset+2);
// Check if there are any letters before or after selected string.
// If not, change before and after to null.
before = before.length >= 1 ? before : null;
after = after.length >= 1 ? after : null;
console.log(before, after);
}
});
<div>this is a test</div>
You don't need jQuery for this. All you need is window.getSelection(). This returns an object. You can get the surrounding text with window.getSelection().anchorNode.data, and get the index of the selection within that text using window.getSelection().anchorOffset. Putting this all together, we have
var selection = window.getSelection();
var selectionText = selection.toString();
var surroundingText = selection.anchorNode.data;
var index = selection.anchorOffset;
var leftNeighbor = surroundingText[index - 1];
var rightNeighbor = surroundingText[index + selectionText.length];
Note that you will get undefined instead of null when there is no neighbor character.
window.addEventListener("click", function(){
var selection = window.getSelection();
var selectionText = selection.toString();
var surroundingText = selection.anchorNode.data;
var index = selection.anchorOffset;
var leftNeighbor = surroundingText[index - 1];
var rightNeighbor = surroundingText[index + selectionText.length];
alert(leftNeighbor + " " + rightNeighbor);
});
<div>
this is a test
</div>
The problem with the other two solutions is they both work if the user selects from left to right, but not if they select right to left. As such, I use this modified version:
const selection = window.getSelection();
const text = selection.anchorNode.textContent;
let beforeChar;
let afterChar;
if (selection.anchorOffset < selection.focusOffset) {
beforeChar = text.substring(selection.anchorOffset, selection.anchorOffset-1);
afterChar = text.substring(selection.extentOffset, selection.extentOffset+1);
} else {
beforeChar = text.substring(selection.extentOffset, selection.extentOffset-1)
afterChar = text.substring(selection.anchorOffset, selection.anchorOffset+1);
}
console.log(beforeChar, afterChar);

Categories

Resources