Find closing tag in HTML string - javascript

I need to select text from different paragraphs and make a span for showing this text. See this example:
<p> this is a text </p>
<p>hello ever one </p>
Now what I want is that if I select text from the web view in my iPhone app it highlights it in a different color. For this I am making a span and setting its style. It works fine for the same paragraph but not for different paragraphs. See this:
<p> this <span class="blue">is a </span> text </p>
Class blue declares its style and it works fine, but the following does not work:
<span class="blue">
<p> this is a text </p>
<p>hello ever </span> one </p>
For solving this problem I need two spans for both paragraphs. So how can I check where the new paragraph starts? The correct HTML code is:
<span class="blue">
<p> this is a text </p></span>
<p> <span class="blue"> hello ever </span> one </p>
I need to get this HTML string but I get the wrong one. I have written a JavaScript function that gets the selection and makes a span according to selection. But on selecting text from two paragraphs it does not work because it gives the wrong section of HTML code. See my JavaScript code:
function highlightsText()
{
var range = window.getSelection().getRangeAt(0);
var selectionContents = range.extractContents();
var div;
var newDate = new Date;
var randomnumber= newDate.getTime();
var imageTag = document.createElement("img");
imageTag.id=randomnumber;
imageTag.setAttribute("src","notes.png");
var linkTxt = document.createElement("a");
linkTxt.id=randomnumber;
linkTxt.setAttribute("href","highlight:"+randomnumber);
div = document.createElement("span");
div.style.backgroundColor = "yellow";
div.id=randomnumber;
linkTxt.appendChild(imageTag);
div.appendChild(selectionContents);
div.appendChild(linkTxt);
range.insertNode(div);
return document.body.innerHTML+"<noteseparator>"+randomnumber+"<noteseparator>"+range.toString();
}
Please provide a solution that can resolve this problem.

You could do something along the lines of:
Get highlighted section of text.
Insert span tag at the first point.
For every tag that you come accross within the highlighted text:
If it's an opening tag, check if it's corresponding closing tag is in the highlighted text.
If both opening and closing tags are within the text ignore them and move to the next point after the corresponding closing tag.
If only the opening tag or only the closing tag is present, then insert before the tag and after the tag.
Insert span closing tag at the end of the highlighted text.
Possible problem:
span is intented to group inline elements and not block elements so if your highlighted text includes block elements you could have problems. You could use div instead of span to solve this or you could add some checks to distinguish between inline and block tags.
To look at tag matching:
http://haacked.com/archive/2004/10/25/usingregularexpressionstomatchhtml.aspx
To find if the matching closing tag of an element is in the higlighted text (not tested):
function checkClosingTag(position)
{
//Find position of next opening or closing tag along the
//string of highlighted text.
//Return 0 if no more tags.
var nextTag = findNextTag(position);
if(nextTag == 0)
{
return 0;
}
if(!isOpeningTag(nextTag))
{
return nextTag;
}
var nextTagClose = checkClosingTag(nextTag);
if(nextTagClose == 0)
{
return 0;
}
return checkClosingTag(nextTagClose);
}
This looks like a fairly involved problem though - I don't have time to write the code for you but you should be able to work out a way of doing it from here.

some change in your code can work
see this line of codes
function highlightsText()
{
var range, sel;
if (window.getSelection)
{
sel = window.getSelection();
if (sel.getRangeAt) {
range = sel.getRangeAt(0);
}
document.designMode = "on";
if (range) {
sel.removeAllRanges();
sel.addRange(range);
}
if ( !document.execCommand("HiliteColor", false, "yellow") ) {
document.execCommand("BackColor", false, "yellow");
}
document.designMode = "off";
}
else if (document.selection && document.selection.createRange) {
range = document.selection.createRange();
range.execCommand("BackColor", false, "yellow");
}
var newDate = new Date;
var randomnumber= newDate.getTime();
var nodeList = document.querySelectorAll(".Apple-style-span");
for (var i = 0, length = nodeList.length; i < length; i++) {
nodeList[i].id = randomnumber;
}
var div = document.getElementById(randomnumber);
var imageTag = document.createElement("img");
imageTag.id=randomnumber;
imageTag.setAttribute("src","notes.png");
var linkTxt = document.createElement("a");
linkTxt.id=randomnumber;
linkTxt.setAttribute("href","highlight:"+randomnumber);
div.appendChild(linkTxt);
range.insertNode(div);
return document.body.innerHTML+"<noteseparator>"+randomnumber+"<noteseparator>"+range.toString();
}
You need make some adjustments in this code.

Since your goal (based on what you stated in your question) is to highlight selected text with a different color, here is a solution to that goal.
The HTML5BoilerPlate project includes styles to control the selection color (line 52 in the style.css file)
Here's the CSS for it:
/* Remove text-shadow in selection highlight: h5bp.com/i
*
* These selection declarations have to be separate
*
* Also: hot pink! (or customize the background color to match your design)
*/
::-moz-selection { background: #fe57a1; color: #fff; text-shadow: none; }
::selection { background: #fe57a1; color: #fff; text-shadow: none; }

Related

execCommand Bold Alternative

As the execCommand() is no longer recommended for new projects, how should I implement it?
What I've tried is to wrap the selected text in a tag and the removing it. It works fine as long as the user doesn't make it bold and then select half of bold and half of non-bold text...it crashes.
function bold(){
var selection = window.getSelection();
var selRange = selection.getRangeAt(0);
if (selection != "") {
let bold = document.createElement("strong");
selRange.surroundContents(bold);
//if it's already bode, the above two lines would have generated another <strong> tag
if (bold.childElementCount>0) {
//removing the child <strong> tag
bold.innerHTML = bold.firstChild.innerHTML;
let parent = bold.parentNode;
text = parent.innerHTML.replace(`<strong>${selection}</strong>`, selection);
parent.innerHTML = text;
}
}
}

Wrap an html tag after clicking a toolbar button using js or jquery

I want to do something similar to what this website and wordpress does. When a user highlights text on the screen, then clicks a button on the toolbar it will wrap an html tag around the text. In jquery I would probably use the .wrap class but how would I detect if the user highlighted something.
For example, when the user writes Hello World then clicks on the bold button it will say <b>Hello World</b>
This mainly requires (1) accessing the selectionStart and selectionEnd properties of the input/textarea element and (2) replacing the substring of the value property across that range with the same text, but wrapped in the desired start and end tags. Also, I think it makes sense to reselect the replaced text, which requires a couple of calls to select() and setSelectionRange(). Also, if there's no selection (meaning start equals end) it's probably a good idea to do nothing at all.
window.selWrapBold = function(id) { selWrap(id,'<b>','</b>'); };
window.selWrapItalic = function(id) { selWrap(id,'<i>','</i>'); };
window.selWrap = function(id,startTag,endTag) {
let elem = document.getElementById(id);
let start = elem.selectionStart;
let end = elem.selectionEnd;
let sel = elem.value.substring(start,end);
if (sel==='') return;
let replace = startTag+sel+endTag;
elem.value = elem.value.substring(0,start)+replace+elem.value.substring(end);
elem.select();
elem.setSelectionRange(start,start+replace.length);
} // end selWrap()
<input type="button" value="bold" onclick="selWrapBold('ta1');"/>
<input type="button" value="italic" onclick="selWrapItalic('ta1');"/>
<br/>
<textarea id="ta1"></textarea>
Get the text of the html element which is wrapping the text, then add as html the text embedded in the <b> tag.
See jQuery DOM Manipulation for tutorials.
I used this question to get the selected text. And this question to
get the element with selected text in it. I combined them in a single function.
function updateHighlightedText() {
var text = "";
if (window.getSelection) {
text = window.getSelection().toString();
} else if (document.selection && document.selection.type != "Control") {
text = document.selection.createRange().text;
}
var node = $(window.getSelection().anchorNode.parentNode); //Get the selected node
node.html(node.text().replace(text, "<b>"+text+"</b>")); //Update the node
}

How can set new content for selected item with javascript?

suppose to be we have a paragraph with this content " Hi , It's a new question in stackoverflow!"
and when we are selecting something in this paragraph , it's turn to be Red .for example we selected stackoverflow & then it turn to <span class="red">stackoverflow</span>.how can we do this with Javascript?
here is my codes :
var x = {};
x.getSelected = function() {
var t = '';
if (window.getSelection) {
t = window.getSelection();
} else if (document.getSelection) {
t = document.getSelection();
} else if (document.selection) {
t = document.selection.createRange().text;
}
return t;
}
$(document).ready(function() {
var selectedText;
$(document).bind("mouseup", function() {
selectedText = x.getSelected()
if (selectedText !=''){
alert(selectedText);
//Now I wanna set new content for selected item but not working
a=selectedText;
selectedText.html("<span class='red'>"+a+"</span>");
}
});
});
.red {
color : red;
}
<p>suppose to be we have a paragraph with this content " Hi , It's a new question in stackoverflow!" and when we are selecting something in this paragraph , it's turn to be Red .for example we selected stackoverflow & then it turn to .how can we do this with Javascript? </p>
...when we are selecting something in this paragraph , it's turn to be
Red...
You could have a stab at the styleWithCSS command of the editing API, execCommand that is.
However, before proceeding please note that:
This spec is incomplete and it is not expected that it will advance
beyond draft status. Authors should not use most of these features
directly, but instead use JavaScript editing libraries. The features
described in this document are not implemented consistently or fully
by user agents, and it is not expected that this will change in the
foreseeable future.... This spec is to meant to help implementations
in standardizing these existing features. It is predicted that in the
future both specs will be replaced by Content Editable Events and
Input Events....
Having clarified that, the following will work in most modern browsers viz. Edge, FireFox and Chrome that I could test in.
By default the foreColor command of execCommand wraps the selected text with a font tag, which is deprecated. So, you need to use the styleWithCSS command. Now this works with the editing API, which means that the element you are trying to work with, should have its contentEditable attribute set.
To work around this, you can temporarily set this attribute just before changing the color in the selected text fragment and then resetting the attribute once done.
Given your paragraph like this:
<p id="p">
Hi , It's a new question in StackOverflow!
</p>
When you select the word StackOverflow, the following code will result in this...
<p id="p">
Hi , It's a new question in <span style="color: rgb(255, 0, 0);">StackOverflow</span>!
</p>
... wrapping your selected text in a span with the style applied.
Fiddle: http://jsfiddle.net/abhitalks/j9w6dj7m/
Snippet:
p = document.getElementById('p');
p.addEventListener('mouseup', setColor);
function setColor() {
p.setAttribute('contentEditable', true);
document.execCommand('styleWithCSS', false, true);
document.execCommand('foreColor', false, "#f00");
p.setAttribute('contentEditable', false);
}
<p id="p" contentEditable="false">
Hi , It's a new question in stackoverflow!
</p>
Edit:
Now that you have added code (and what you have already tried) in your question, you could use the range selection to do what you are after.
Specifically, you will need to learn:
selection: https://developer.mozilla.org/en-US/docs/Web/API/Selection, this you have already done. Cheers!
range: https://developer.mozilla.org/en-US/docs/Web/API/Range/Range, because you will be dealing with ranges here
selection.getRangeAt(): https://developer.mozilla.org/en-US/docs/Web/API/Selection/getRangeAt, because you will need to extract the selected text as a range object
range.surroundContents(): https://developer.mozilla.org/en-US/docs/Web/API/range/surroundContents, because you will need to surround the selected text range with a span.
Putting it all together all you have to do is (explanation in code comments):
function setClass() {
var selection = x.getSelected(), range, // you have already done this
span = document.createElement("span"); // create a span element
span.classList.add('red'); // add the class to the span
if (selection != '') {
range = selection.getRangeAt(0); // get the range from selected text
range.surroundContents(span); // surround the range with span
}
}
Fiddle 2: http://jsfiddle.net/abhitalks/kn0u5frj/
Snippet 2:
var x = {},
p = document.getElementById('p');
p.addEventListener('mouseup', setClass);
function setClass() {
var selection = x.getSelected(), range,
span = document.createElement("span");
span.classList.add('red');
if (selection != '') {
range = selection.getRangeAt(0);
range.surroundContents(span);
}
}
x.getSelected = function() {
var t = '';
if (window.getSelection) {
t = window.getSelection();
} else if (document.getSelection) {
t = document.getSelection();
} else if (document.selection) {
t = document.selection.createRange().text;
}
return t;
}
.red { color: #f00; }
<p id="p">
Hi , It's a new question in stackoverflow!
</p>
You can use the getSelection() method
Below is the example:
Repeated Question:
How to get selected text in textarea?
You can use CSS with :: selection http://caniuse.com/#search=%3A%3Aselection
::selection {
background: #ffb7b7; /* WebKit/Blink Browsers */
}
::-moz-selection {
background: #ffb7b7; /* Gecko Browsers */
}
Or javascript with range

How to know if Ckeditor anchor is encapsulated within span?

This is specifically for the developers of CKEditor.
The goal is to know when the cursor is either inside or outside a span of custom atttributes.
When using the Ckeditor, if inserted a custom span like below from a plug-in, occassionally when you stop typing, un focus the textarea and replace your cusor to the end of the line - as though you are continuing typing where you had left off. The text can either be inside the span or outside with no encapsulation.
Example before unfocus:
<span style="color:blue;line-height:12px;font-size:10px;font-family:arial;" class="master-span">
<span style="color:red;line-height:20px;font-size:18px;font-family:museo;" class="child-span">Text is here</span>
</span>
Re-Focus and positioning cusor on screen at the end of "here" and start typing.
<span style="color:blue;line-height:12px;font-size:10px;font-family:arial;" class="master-span">
<span style="color:red;line-height:20px;font-size:18px;font-family:museo;" class="child-span">Text is here</span> Text is now outside!!!
</span>
What I'm asking is that where would I target my troubleshoot to know when the cursor/anchor is within the span or outside of it i.e. Is there a calculation being made in the code to determine this?
The reason for this enquiry is that the span formmating is extremely important and we can't use a master span in the surrounding textarea as this can be altered by the user. We find also, that using bulletpoints are troublesome due to the browsers inability to set line-height so ensuring text is always within the span would be a major help.
P.S I've downloaded the source code to see where I could find it.
Many Thanks for any help.
Upon clicking of the text area of any of my CKeditor instances I looked for the parent of the cursor to see if it was an LI node. This indicating that the cursor had landed outside of my span within a bullet point. I proceeded to find the most outside Text Node I could find as my assumption is that if the cursor was outside the span thus the user wanted to continue typing from the last character.
In my setup, within a span there can be a number of multiple sub spans with no limitation of number. So I had to find the most outside span that had a text node at the end (or close to the end).
I did by getting the Node List and reversing the order to find the first text node within the reverse order. I created a function that could loop the number of span and break upon finding the first text node.
Once I had my text node I then set up ranges and calculated the length of the text in order to place it at the end. Below is my final solution which works on latest Chrome/FF/IE.
$("#preview-text-3827").on("click", function(){
var CKEDITOR = window.parent.CKEDITOR;
var ck_instance_name = false;
for ( var ck_instance in CKEDITOR.instances ){
if (CKEDITOR.instances[ck_instance].focusManager.hasFocus){
ck_instance_name = ck_instance;
break;
}
}
var editor = CKEDITOR.instances[ck_instance_name];
var selection = editor.getSelection();
var parent_attrs = "";
if (selection.getType() == CKEDITOR.SELECTION_TEXT) {
parent_attrs = selection.getStartElement();
if(parent_attrs.getName() == "li"){
var nodeList = parent_attrs.getChildren();
console.log( "Now Reverse" );
for ( var i = nodeList.count() - 1; i > -1; --i ) {
//console.log( nodeList.getItem( i).nodeName );
var el = nodeList.getItem( i);
if(el.$.nodeName == "#text"){
// This is the last text but it's inside the li which is WRONG
}
if(el.$.nodeName == "SPAN"){
// This should be the last span
// Jump into this span and find the children.
var result = retractLiPosition(el);
if(result.text == true){
var inner = result.result;
var text = inner.$.textContent || inner.$.innerText;
var length = text.length;
var range = editor.createRange();
range.setStart( inner , length);
range.setEnd( inner, length);
editor.getSelection().selectRanges( [ range ] );
break;
}
}
}
}
}
});
function retractLiPosition(element){
var returning = new Object();
returning.result = element;
returning.text = false;
var result = element;
var nodeList = element.getChildren();
for ( var i = nodeList.count() - 1; i > -1; --i ) {
var el = nodeList.getItem( i);
if(el.$.nodeName == "#text"){
returning.result = el;
returning.text = true;
break;
}
if(el.$.nodeName == "SPAN"){
// This should be the last span
// Jump into this span and find the children.
returning = retractLiPosition(el);
}
}
return returning;
}
});

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.
});

Categories