Function does not work on second occurence? - javascript

I am trying to change the color of multiple texts to a certain color by using this code:
var search = "bar";
$("div:contains('"+search+"')").each(function () {
var regex = new RegExp(search,'gi');
$(this).html($(this).text().replace(regex, "<span class='red'>"+search+"</span>"));
});
However, the code does not work a second time, and I am not sure why--it only changes the newest occurrence of the code.
Here is a JSFiddle using it twice where it is only changing the 2nd occurrence: http://jsfiddle.net/PELkt/189/
Could someone explain why it does not work on the 2nd occurrence?

Could someone explain why it does not work on the 2nd occurrence?
By calling .text() you are removing all the HTML markup, including the <span>s you just inserted.
This is the markup after the first replacement:
<div id="foo">this is a new <span class='red'>bar</span></div>
$(this).text() will return the string "this is a new bar", in which replace "new" with a <span> ("this is a <span class='red'>new</span> bar") and set it as new content of the element.
In order to do this right, you'd have to iterate over all text node descendants of the element instead, and process them individually. See Highlight a word with jQuery for an implementation.

It was easy to fix your jsfiddle. Simply replace both .text() with .html() & you'll see that it highlights new & both bars in red.
jQuery's .text() method will strip all markup each time that it's used, but what you want to do is use .html() to simply change the markup which is already in the DOM.
$(document).ready(function () {
var search = "bar";
$("div:contains('"+search+"')").each(function () {
var regex = new RegExp(search,'gi');
$(this).html($(this).html().replace(regex, "<span class='red'>"+search+"</span>"));
});
search = "new";
$("div:contains('"+search+"')").each(function () {
var regex = new RegExp(search,'gi');
$(this).html($(this).html().replace(regex, "<span class='red'>"+search+"</span>"));
});
});

Here is another way of doing it that will allow you to continue using text if you wish
function formatWord(content, term, className){
return content.replace(new RegExp(term, 'g'), '<span class="'+className+'">'+term+'</span>');
}
$(document).ready(function () {
var content = $('#foo').text();
var change1 = formatWord(content, 'bar', 'red'),
change2 = formatWord(change1, 'foo', 'red');
alert(change2);
$('body').html(change2);
});
http://codepen.io/nicholasabrams/pen/wGgzbR?editors=1010

Use $(this).html() instead of $(this).text(), as $.fn.text() strips off all the html tags, so are the <span class="red">foo</span> stripped off to foo.
But let's say that you apply same highlight multiple times for foo, then I would suggest that you should create a class similar to this to do highlighting
var Highlighter = function ($el, initialArray) {
this._array = initialArray || [];
this.$el = $el;
this.highlight = function (word) {
if (this.array.indexOf(word) < 0) {
this.array.push(word);
}
highlightArray();
}
function highlightArray() {
var search;
// first remove all highlighting
this.$el.find("span[data-highlight]").each(function () {
var html = this.innerHTML;
this.outerHTML = html;
});
// highlight all here
for (var i = 0; i < this._array.length; i += 1) {
search = this._array[i];
this.$el.find("div:contains('"+search+"')").each(function () {
var regex = new RegExp(search,'gi');
$(this).html($(this).html().replace(regex, "<span data-highlight='"+search+"' class='red'>"+search+"</span>"));
});
}
}
}
var highlighter = new HighLighter();
highlighter.highlight("foo");

Related

Update highlight of sentences in TinyMCE on the fly

Okay. I've used Markjs.io and TinyMCE to create a little tool highlighting long sentences (above n-amount of words).
I currently have the text highlighted correctly on load.
I then tried to add som .on('keyup',function()) and .on('change', function()) to run the function and recalculate the highlights / marks as I type.
However. This approach apparently keeps returning the original text. And I can't add any new text to the field.
What I wanna do:
So what I wanna do is figure out how my function should run to keep recalculating and highlight long sentences. But in a way, so that it actually also add in the new text I'm typing or current edits I do in the TinyMCE editor.
Available on CodePen here: http://codepen.io/MarkBuskbjerg/pen/rWWRbX
HTML:
<div id="myTextArea" contenteditable="true">
Any text will do. Above 16 words in a single sentence - from dot to dot - will be highlightet for all the world to see.
</div>
JavaScript (jQuery):
tinymce.init({
selector: '#myTextArea',
height: 300,
setup: function(ed) {
ed.on('change', myCustomInitInstance);
ed.on('keyup', myCustomInitInstance);
ed.on('paste', myCustomInitInstance);
ed.on('cut', myCustomInitInstance);
},
init_instance_callback: "myCustomInitInstance",
});
function myCustomInitInstance(inst) {
var rawText = tinyMCE.get('myTextArea').getContent({
format: 'text'
});
var sentenceArray = rawText.split(".");
var matchWarning = [];
var longSentence = 16;
var words;
var wordCounter;
var output;
for (var i in sentenceArray) {
words = sentenceArray[i].split(" ");
wordCounter = words.length;
if (wordCounter > longSentence) {
matchWarning.push(sentenceArray[i]);
}
}
var $clone = $("#myTextArea").clone();
$clone.mark(matchWarning, {
"separateWordSearch": false,
});
tinyMCE.activeEditor.setContent($clone.html());
}):
The issue is the line:
var $clone = $("#myTextArea").clone();
Which just gets the original value from the textarea each time which is why it isn't updating.
Using makejs on the the body element of the WYSIWYG iframe instead should work:
function myCustomInitInstance(inst) {
var rawText = tinyMCE.get('myTextArea').getContent({
format: 'text'
});
var sentenceArray = rawText.split(".");
var matchWarning = [];
var longSentence = 16;
var words;
var wordCounter;
var output;
for (var i in sentenceArray) {
words = sentenceArray[i].split(" ");
wordCounter = words.length;
if (wordCounter > longSentence) {
matchWarning.push(sentenceArray[i]);
}
}
var editor = tinyMCE.activeEditor;
// Store the selection
var bookmark = editor.selection.getBookmark();
// Remove previous marks and add new ones
$(editor.getBody()).unmark().mark(matchWarning, {
acrossElements: true,
"separateWordSearch": false,
});
// Restore the selection
editor.selection.moveToBookmark(bookmark);
}

Making a part of an uploaded text file as clickable using javascript

I uploaded one file using javascript. I want to make some parts of the text file as highlighted as well as clickable. For example: I want to make all the "hello" in the uploaded file as clickable and highlighted.
I am able to highlight the text as i have used button tag and changed its background and border property in css but I am unable to do an onclick action when the button is clicked.
I tried it like this:
var sel_data=$("#sel").text(); // for taking the text file in avariable
var str='hello';
//making the regular expression of str
var re = new RegExp(str,"g");
//For replacing the 'str' by highlighted and clickable 'str'
var re_str="<button class='highlight' id='ty' onclick='alertfunc()' value="+str+">"+str+"</button>"
//replacement of 'str' by highlighted and clickable 'str'
var rep_data=sel_data.replace(re,re_str);
sel_data=rep_data;
//function to be executed when the button will get clicked
function alertfunc() {
alert('yellow');
}
I also tried it like this
var str='hello'
var re_str="<button class='highlight' id='ty' value="+str+">"+str+"</button>"
$(".highlight").click(function(){
alert("yellow");
})
or like this
var button = document.getElementById("ty");
button.onclick = function(){
alert("yellow");
}
But none of them is working , Please suggest
I referred the above examples by this link: Html make text clickable without making it a hyperlink
There are just a few things wrong here.
First, execute this code on document ready :
$(document).ready(function(){
// code
});
Then, update the actual html in the DOM :
//replacement of 'str' by highlighted and clickable 'str'
var rep_data=sel_data.replace(re,re_str);
sel_data=rep_data;
$("#sel").html(sel_data); // here
And use event delegation for the click :
$("#sel").on('click', '.highlight', function(){
alert("yellow");
});
demo
This is done by using jQuery library and the ready snippet :contains.
Here is the code you need:
jQuery.fn.highlight = function (str, className) {
var regex = new RegExp(str, "gi");
return this.each(function () {
$(this).contents().filter(function() {
return this.nodeType == 3 && regex.test(this.nodeValue);
}).replaceWith(function() {
return (this.nodeValue || "").replace(regex, function(match) {
return "<span class=\"" + className + "\">" + match + "</span>";
});
});
});
};
Using this snippet will lead the words "hello" to be wrapped around a span with class of your choice.
Now to call this function all you need to do is:
$(".testHighlight *").highlight("hello", "highlight");
Ofcourse you have to setup the .testHighlight class with CSS to be something like:
.testHighlight {
background:yellow;
}
To make them clickable you can do it easily with jQuery:
$(.testHighlight).click(function(){
//YOUR CODE HERE
});
You can check more on this snippet here.

Determining a character of a sentence when clicked on

On a random break I found myself wondering if it would be possible to use jQuery to determine a single character within a sentence when it is clicked on.
For example:
This
When the user clicks on first h, jQuery would return this to me.
The only way I could think of doing this would be to wrap each character within the sentence in a span with a class of its letter such as the following example:
<span class="clickable T">T</span>
<span class="clickable h">h</span>
<span class="clickable i">i</span>
<span class="clickable s">s</span>
Followed by a $('.clickable').click(function()) that would return its second class.
My question is: is this the most efficient way to do this?
Obviously wrapping every single letter of the document in span tags is not efficient.
I was able to spin something up that works in Chrome at least. Basically, when you click on a letter, it then triggers a double clicks which selects the word. We get the selection which actually gives us the text of the entire target element. From that, we get the letter that was clicked. We remove the selection and do what we want with the letter.
Fiddle here
$(function(){
$(document).click(function(e){
var target = e.target;
$(target).dblclick();
}).dblclick(function(){
var selection,
node,
text,
start,
end,
letter;
if (window.getSelection) {
selection = document.getSelection();
node = selection.anchorNode;
if (node.nodeType === 3) {
text = node.data;
start = selection.baseOffset;
end = selection.extentOffet;
if (!isNaN(start)) {
letter = text.substr(start, 1);
}
}
window.getSelection().removeAllRanges()
} else if(document.selection) {
//continue work here
}
if (letter) {
alert(letter);
}
});
});
You could return the innerHTML as well with:
$('.clickable').on('click', function(){
alert($(this).html());
});
As for a more efficient way to do it...maybe try this:
in Javascript/jQuery, how to check a specific part of a string and determine if it is a whitespace or letter?
You can do it with this script
$('.clickable').on('click', function(){
var html = $(this).text(); // if you want the text inside the span
var index = $(this).index(); // if you want the position among siblings
var classes = $(this).attr('class').split(" ");
var secondClass = getSecondClass(classes);
});
function getSecondClass(classArray){
if(classArray.length<2){
return null;
}else{
return classArray[1];
}
}
I've also included the html and index variables if you want to do something else with the clicked element.
Basically you split the classes of the element by spaces and then check if the array has less than two elements, in that case it returns null, otherwise it returns the second element.
jsFiddle
Well wrapping all text dyanamically with span tag , it is possible what you were looking for
JS
$(function(){
var lengthText = $('#singlecharacter').text().length;
var textValue = $('#singlecharacter').text();
var textArray = textValue.split('');
var newText = new Array();
for (var i = lengthText - 1; i >= 0; i--) {
newText[i] = "<span class='sp'>"+textArray[i]+"</span>";
};
$('#singlecharacter').html(newText);
$('.sp').click(function()
{
alert($(this).text());
});
});
HTML
<div id='singlecharacter'>THIS</div>
DEMO JSFIDDLE

Select words by js in search result

I am using jQuery and creating now search. And i want to change all words that match of search.
For example:
i type in textbox "hello world", Script get all words "hello" and "world" on page and replace it on "<b class="search_word">hello</b>" and "<b class="search_word">word</b>".
Is it possible?
PS.
Search in div but div contains many other html elements.
now i am using this:
if ($("#search_input").val() != "") {
var words = $("#search_input").val().split(/\s/g)
for (var i = 0; i < words.length; i++) {
var new_reg = new RegExp(words[i], 'gi');
var replaced = $(".content_area").html().replace(new_reg, "<b class='searched_word'>"+words[i]+"</b>")
$(".content_area").html(replaced);
}
}
it works but tags...
if i search something like: "b class is the best class in School" it destroy page structure
Ahh ok, I understand, "onpage" search... hm....
this is a copy from How do I select text nodes with jQuery?
var getTextNodesIn = (function() {
function textNodeFilter() {
return this.nodeType == 3;
}
return function(el) {
var $el = $(el);
return $el
.contents()
.filter(textNodeFilter)
.add(
$el
.find("*")
.contents()
.filter(textNodeFilter)
);
};
})();
you can now use
getTextNodesIn("body").each(function() {
var txt = $(this).text();
$(this).text(txt.replace(new RegExp(myWord,"g"), "<b>" + myWord + "</b>"));
});
Loop now through all of them and aply my filter previously written.
There where you found your word, you must select the whole text, and replace the found word with found Word

How to select a part of string?

How to select a part of string?
My code (or example):
<div>some text</div>
$(function(){
$('div').each(function(){
$(this).text($(this).html().replace(/text/, '<span style="color: none">$1<\/span>'));
});
});
I tried this method, but in this case is selected all context too:
$(function(){
$('div:contains("text")').css('color','red');
});
I try to get like this:
<div><span style="color: red">text</span></div>
$('div').each(function () {
$(this).html(function (i, v) {
return v.replace(/foo/g, '<span style="color: red">$&<\/span>');
});
});
What are you actually trying to do? What you're doing at the moment is taking the HTML of each matching DIV, wrapping a span around the word "text" if it appears (literally the word "text") and then setting that as the text of the element (and so you'll see the HTML markup on the page).
If you really want to do something with the actual word "text", you probably meant to use html rather than text in your first function call:
$('div').each(function(){
$(this).html($(this).html().replace(/text/, '<span style="color: none">$1<\/span>'));
// ^-- here
}
But if you're trying to wrap a span around the text of the div, you can use wrap to do that:
$('div').wrap('<span style="color: none"/>');
Like this: http://jsbin.com/ucopo3 (in that example, I've used "color: blue" rather than "color: none", but you get the idea).
$(function(){
$('div:contains("text")').each(function() {
$(this).html($(this).html().replace(/(text)/g, '<span style="color:red;">\$1</span>'));
});
});
I've updated your fiddle: http://jsfiddle.net/nMzTw/15/
The general practice of interacting with the DOM as strings of HTML using innerHTML has many serious drawbacks:
Event handlers are removed or replaced
Opens the possibility of script inject attacks
Doesn't work in XHTML
It also encourages lazy thinking. In this particular instance, you're matching against the string "text" within the HTML with the assumption that any occurrence of the string must be within a text node. This is patently not a valid assumption: the string could appear in a title or alt attribute, for example.
Use DOM methods instead. This will get round all the problems. The following will use only DOM methods to surround every match for regex in every text node that is a descendant of a <div> element:
$(function() {
var regex = /text/;
function getTextNodes(node) {
if (node.nodeType == 3) {
return [node];
} else {
var textNodes = [];
for (var n = node.firstChild; n; n = n.nextSibling) {
textNodes = textNodes.concat(getTextNodes(n));
}
return textNodes;
}
}
$('div').each(function() {
$.each(getTextNodes(this), function() {
var textNode = this, parent = this.parentNode;
var result, span, matchedTextNode, matchLength;
while ( textNode && (result = regex.exec(textNode.data)) ) {
matchedTextNode = textNode.splitText(result.index);
matchLength = result[0].length;
textNode = (matchedTextNode.length > matchLength) ?
matchedTextNode.splitText(matchLength) : null;
span = document.createElement("span");
span.style.color = "red";
span.appendChild(matchedTextNode);
parent.insertBefore(span, textNode);
}
});
});
});

Categories

Resources