Javascript/jQuery substring highlighting problem - javascript

I am using Javascript to calculate the offset and length of a substring selected on my page. This gets stored somewhere and later, when I hover over the text I want to highlight certain words in it using that offset and length in jQuery. Here is the basic code used for the highlight:
content = $("#reader").html();
newContent = content.substring(0,offset)+'<font style="color: red;">'+content.substring(offset,offset+length)+'</font>'+content.substring(offset+length,content.length);
content = $("#reader").html(newContent);
Now my problem is this: the offset and length are calculated over what is displayed on the screen. The actual HTML code, however, may also contain <p> or other tags. As a result, the text that I highlight gets "shifted", depending on the amount of HTML code present.
What is the easiest way to solve this?

This is not easily solvable. You shouldn’t calculate offset and length over the text, but over the HTML, if you want to work on the HTML afterwards. However, inserting HTML tags somewhere into an existing HTML string may lead to invalid nesting. And finally, you lose state like attached event handlers if you re-insert nodes as HTML code.

use
$("#reader").text()
instead of $("#reader").html() to get the text out of your content

I'd strip tags or use DOM methods - like. .nodeType, .nodeValue.

Related

Is there a way to apply css to part of the element?

I am looking for a way to apply new CSS to only part of the element.
For example. The original HTML looks like
<p>123456</p>
I want to make only 456 into bold.
Of course, I can do it by adding another tag into 456 like
<p>123<b>456</b></p>
But in my application, I do want not to change the original DOM structure. By adding a new tag, I changed the DOM structure.
To do that, I am thinking of adding new custom attribute to the existing tag like
<p data-wms="e-3">123456</p>
Here data-wms means that there are special part and e-3 means that from index 3 character (it is 4 here) to the end will have a special attribute (like bold in this example)
Now I have all the information about where to change inside the element.
But still, how can I do that with javascript without adding a tag, without changing dom.
Thanks
You can use the span element to do so, it's made specifically to handle inline styling while mantaining the overall structure.
An example would be:
<p>123<span class="bold-highlight">456</span></p>
Thanks to everyone's advice, I researched more, especially about nth-letter.
Though nth-letter is exactly what I want, I found that it is still just proposal, not implemented in any browser.
Thus, there is no way to applying different css letter by letter in one text element without embracing each letter with span tag at this moment (2021-March). I hope that there will be nth-letter in the near future.
I think that I have to re-design my project...
if it's a static page and you want to change a style for specific text in a specific tag like the following case
<p>11111</p>
<p>22222</p>
<p>33333</p>
<p>44444</p>
let's say you want just style the third element, you can change it by the following code using jQuery for sure you can use JavaScript but jQuery will help you to make your code shorter
$( "p:nth-child(3)" ).css("color","#f00");

when editing script elements code, should it be edited with textContent or innerHTML? or something else?

for example,
function browserInjectCode(code){
let ele=document.createElement("script");
ele.textContent=code;
document.head.appendChild(ele);
}
browserInjectCode("console.log(\"js code may contain html elements\");\n\/\/ <this><is><a><comment>");
given that javascript may contain html elements, i wonder, should code in script-elements be edited with .innerHTML or .textContent ? should the above function use ele.textContent=code; or ele.innerHTML=code; ? is there even a difference?
From the docs:
Differences from innerHTML
innerHTML returns HTML, as its name indicates. Sometimes people use
innerHTML to retrieve or write text inside an element, but textContent
has better performance because its value is not parsed as HTML.
Moreover, using textContent can prevent XSS attacks.
You can use either, but it depends on your use-case.
Basically, innerHTML is meant to keep the html qualities of the code (as the name suggests), including html tags within your code (like keeping the functionality "strong" in a "p" tag). Example: If you had the following div;
<div id="hi"><strong>hi hello</strong></div>
and got the innerHTML value it would give you the following;
<strong>hi hello</strong>
innerContent would not keep an html element's value but would keep the content of the div;
hi hello
Another Javascript tool called innerText would simplify further by giving you the text but would not give you the extra space put between the text;
hi hello
I think that if you are adapting the contents of a script tag it would make the most sense to use innerContent. I hope this is helpful. If you want a more detailed explanation, go the following link; https://medium.com/better-programming/whats-best-innertext-vs-innerhtml-vs-textcontent-903ebc43a3fc.

strange behavior of html tags inside contenteditable div

In my project, I am trying to add HTML tag letter (< and >) dynamically to a contenteditable div. Whenever user is pressing alphanumeric character, I am appending an empty span element which is used for calculating the position of the caret in contenteditable div.
The problem is that when I type some words like following:
when <
and press a alphanumeric character like b (which calls a function to append a span element), The contenteditable div is showing just when instead of when <b.
When I inspected the element I found the contenteditable div has the following content:
when <b<span class="spanPos"></b<span>
^ strange that span is holding '</b' instead of being empty
Here is a example JSFiddle.
I am not sure how this is happening. Please tell me what should I do to evade this issue.
PS: Here I am trying to add < and >, not HTML elements like <b></b>.
As you said, you're trying to add a symbol which can be interpreted as HTML. You need to escape it or use a different way to express it as an ISO entity:
$('#btnContent').click(function(){
$('#content').html("when <b" + "<span class='spanPos'></span>");
});
http://jsfiddle.net/Dekku/jXeVW/1/
I have visit your code and found there is a <b in JS don't know by are you using that.
Use the below code:
$('#btnContent').click(function(){
$('#content').html("when" + "<span class='spanPos'></span>");
});
By removing the <b tag you will not get this. or You should properly close to it to make the code proper.
EDITED:
After visiting your new code I got the solution you should use the following code:
$('#btnContent').click(function(){
$('#content').html("when <b");
$('#content').html($('#content').html() + "<span class='spanPos'></span>");
});
If you need the details then tell me.
The problem was actually that I was appending a span element to the text whenever the user types something in to the contenteditable div to grab the position of the caret. Something like this
Hello I am he|re
^ Caret position
Then I was adding a span element in between of that text using .html() something like this
Hello I am he<span></span>re
Now whenever I add any letter which represents Html like < or >, then it was merging some of the text inside the span (as I shown above in my post).
So, I solved it by removing it just after when I get the position through span element. I followed the below steps to solve this issue for each letter pressed by the user.
//Store the .text() in a variable.
//Replace Html letters inside the contenteditable div with alphabets which has same width. (for exact positioning).
//add span to the modified text through .html()
//get the offset position of the span
//Do something with that offset position
//Remove span
//Replace the .text() inside the contenteditable div with the text which was stored in a variable.
I did these many things to solve this issue because I was using .text() everywhere else in my project code. If I change it to .html() then I must have rewritten the complete code and might also can't complete the project.
Clearly, I couldn't have done the things which were mentioned in the other answered posts. I hope this will help someone.

Merging Text Nodes Together After Inserting Span

I have an extension where I am storing/retrieving a section of the DOM structure (always a selection of text on the screen) the user has selected. When I am storing a selection, I enclose the section in a SPAN tag, and highlight the text in yellow. This causes the DOM structure around the selected text to split up into various text nodes. This causes a problem for me as when I try to restore this selection (without refreshing the page) it causes problems as the DOM structure has been modified.
My question is how do I prevent the DOM structure from splitting up after inserting the SPAN? If this cannot be achieved, how would I reassemble the DOM structure after removing the SPAN tag to its original state?
//Insert the span
var sel = restoreSelection(mootsOnPage[i].startXPath);
var range = sel.getRangeAt(0).cloneRange();
var newNode = document.createElement('span');
newNode.className = 'highlightYellow';
range.surroundContents(newNode);
//Original DOM structure
<p>Hello there, how are you today</p>
//What the DOM looks like after insertion of SPAN
<p>
"Hello there, "
<span class="highlightYellow">how</span
" are you today"
</p>
Use element.normalize().
After you remove the span you inserted, you can use the element.normalize() method to merge the extra text nodes that were created as a result of the insertion/removal of the span. The normalize() method puts the specified element and all of its subtree into a "normalized" form (i.e. no text nodes in the subtree are empty and there are no adjacent text nodes). Found, thanks to #tcovo's comment.
Text nodes inside of an element are broken apart if you insert nodes and then remove them. Unfortunately they don't automatically re-merge once the extra node is removed. To answer peoples' questions as to "why" this matters, it usually causes issues when working with text highlighting in your UI.
The very act of inserting a <span> tag will alter the DOM. That's, somewhat by definition, what you're doing when you call surroundContents(). You can't add a span tag without altering the DOM which includes splitting text nodes and adding new elements for the span.
Further, unless the selected text includes only whole text nodes and the selection never starts/stops in the middle of a text node, you will have to split text nodes to put the span in the right place. When you later remove the span tags, you will have extra text nodes. That shouldn't really matter to anything, but if you really think you have to get the split text nodes back to the way they were, I can think of a couple options:
1) Save the original parentNode before the span is inserted into it. Clone it, add your span to the clone, replace the original node with the clone and save the original. When you want to restore, put the original back and remove the cloned one.
2) When you remove the span, run a function that looks for neighboring text nodes and combine them.
3) Figure out why it matters that there are more text nodes afterwards than there were before because this should not matter to any code or display.
When using normalize() pay attention!
It will strip away nodes like <br/> and will alter the text and its visualisation.
normalize() is good, but it has its drawbacks.
So <p>"this is an "<br/>"example"</p> will turn into <p>this is an example</p>
Is there a way to use normalize() but keeping the <br/>s?
You can concatenate and then remove the second node
node1.textContent += node2.textContent;
node2.remove();
You can use this to unwrap your content.
$(".highlightYellow").contents().unwrap();
Demo: http://jsfiddle.net/R4hfa/

HTML + Javascript: Detecting where in a line of text a click occurred

What's a good way to do this without wrapping each letter with <span> tags and binding onclick functions to each, or something silly like that?
if you use a textarea and style it to not look like one, you could do something like this: Inserting a text where cursor is using Javascript/jquery
I don't think that's possible as each browser renders a page different on each client. As a javascript-event is always attached to some html tag (and a text it self is not a tag but the content of this tag) you can only retrieve the tag the event occures on or every parent tag.
You could in some way determine the approx. position of the cursor by wrapping each paragraph or sentence with div||p||span, retrieve the position of the click event relative to the document, retrieve the position of the tag the event occures on relative to the document and calculate depending on the number of sibling tags and their heights the approx. line number.

Categories

Resources