Replace all occurrences of an array of words in page - javascript

I'm trying to highlight all occurrences of an array of words anywhere on the page, regardless of their parent element.
I've adapted the following question's solution and it works great if I know the selector to use the function on. However, the words could appear in ANY selector, so I'm trying to use a wildcard selector, but it's not working.
(function($) {
var keywords = ['Breathalyzer', 'Marijuana', 'Alcohol'];
function highlightWords(element) {
full_text = element.html();
$.each(keywords, function(i) {
full_text = full_text.replace(RegExp(keywords[i], "gi"), "<span class='highlighttext'>"+keywords[i]+"</span>");
});
element.html(full_text);
}
highlightWords($("*"));
})(jQuery);
I'm using a regexp that's case insensitive so that it matches the search terms regardless of case. And I'm expecting the function to wrap all matched terms in <span class="highlighttext"></span> but it's not working...
EDIT: I guess I should add that I'm not getting any sort of errors in the console

Answer:
There are a few things that need to be changed for this to work as you'd expect:
$("*") returns a collection. It's an array of elements, not a single element. Therefore passing the array into a function expecting a single element will not work.
Solution:
We can fix the above by using JQuery's .each function to iterate over each item in the collection and then calling your highlight function.
We have to adjust your highlightWords function to be on the receiving end of the each method.
Solution:
We change the parameters to match what's passed
in. This is why instead of element, we use index, element.
.html is a JQuery method. We need to be sure that the element variable that we receive contains this method or we won't be able to use it.
Solution:
Since the element is not a JQuery object, we wrap the element within a JQuery object by passing the element variable in as a selector. element = $(element);
Working Code:
(function($) {
var keywords = ['Breathalyzer', 'Marijuana', 'Alcohol'];
function highlightWords(index, element) {
element = $(element);
full_text = element.html();
$.each(keywords, function(i) {
full_text = full_text.replace(RegExp(keywords[i], "gi"), "<span class='highlighttext'>"+keywords[i]+"</span>");
});
element.html(full_text);
}
$("*").each(highlightWords);
})(jQuery);
.highlighttext {
color: green;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div>
<p>Hello Alcohol</p>
<p>This is a test of <code>marijuana</code></p>
<small>Maybe we should get a Breathalyzer?</small>
<h3>No, no, we should get more Alcohol.</h3>
</div>

Related

How to extract text from all elements of given class under given HTML element with jQuery

When I do this:
var elem = $(".region_box");
text = elem.find(".my_text").html();
I can get text from the first element deep below "elem" with class "my_text" which it finds.
But there are many elements of the class "actual_text" below elem and I want to combine the text from all of them. How would I do it?
I am new to jQuery and I searched around for solution for long time, but neither of them worked for me. Perhaps I am using this each() function incorrectly. I would very much appreciate any help.
You can use the jQuery.each
var text = '';
var elms = $(".region_box .my_text").each(function () {
text = text + $(this).text(); // use .html() if you actually want the html
});
Define a variable to hold the new string, select all of the elements you wish to extract, chain that with .each() and add the contents of the text node for that element to the variable you created to hold the new string.
(Demo)
var text = "";
$(".region_box .my_text").each(function(){ text += " " + $(this).text(); });
try something using jquery .map() method like this,
text = $('.region_box').find('.my_text').map(function() {
return $(this).text();
}).get().join(',');
As the site said, "As the return value is a jQuery object, which contains an array, it's very common to call .get() on the result to work with a basic array."

jQuery append() for multiple elements after for loop without flattening to HTML

I have a loop:
for (index = 0; index < total_groups; index += 1) {
groups[index].list_item = $(list_item_snippet);
// Closure to bind the index for event handling
(function (new_index) {
groups[index].list_item.find('.listing-group-title')
.html(groups[index].Group.Name)
.click(function(e){
fns.manageActiveGroup(new_index, groups);
return false;
});
})(index);
// Append to DOM
mkp.$group_listing.append(groups[index].list_item);
};
I would rather not call append() each time the loop fires.
I know that I could use a String and concatenate the markup with each loop iteration and append the string to mkp.$group_listing at the end, however this flattens the object and the bindings are lost (I am not relying on IDs).
Is there a way to perhaps add my objects to an array and append them all in one go at the bottom without flatening to HTML?
Assumptions:
$(list_item_snippet) contains some HTML defining a list item (and includes an element with class .listing-group-title).
groups is a block of JSON defining a 'group' in my script
The closure works perfectly
Edit:
Found that I can use the following syntax to append multiple elements:
mkp.$group_listing.append(groups[0].list_item, groups[1].list_item );
But i obviously need to automate it - it's not an array it's just optional additional function parameters so I'm not sure how to do this.
To append an array of elements to a selector you can use this:
$.fn.append.apply($sel, myArray);
In your case, since it's actually the .list_item property of each array element that you need you can use $.map to extract those first:
$.fn.append.apply(mkp.$group_listing, $.map(groups, function(value) {
return value.list_item;
}));
Instead of bind it the way you've done, if you bind it using on() like below,
$(document).on('click', '.listing-group-title', function() {
// click handler code here
});
You can flatten the HTML and append it in one statement and it'll still work.
Note: For better efficiency, replace document in the above statement to a selector matching the closest parent of .listing-group-title
Yes. Use the jQuery add method to add all your items to a jQuery object. Then append that one object.
http://api.jquery.com/add/
EDIT: Example:
var arr = $();
for (index = 0; index < total_groups; index += 1) {
groups[index].list_item = $(list_item_snippet);
// Closure to bind the index for event handling
(function (new_index) {
...
})(index);
// Add to jQuery object.
arr.add(groups[index].list_item));
};
mkp.$group_listing.append(arr);

jQuery find() Issue

I have the following Code
var page = '<h1>Hello!</h1><p>FOO</p><span class="username">this is ur name</span><p>sample text</p>';
when I alert $(page).html() i get Hello! and when I wrap the contents of the page in a div and alert $(page).html() I get the whole html contents.
What I am trying to accomplish here is I have a page string with html template and I am trying to find a class username in it and i am getting null.
I am confused with this happens
Here is a small fiddle of the issue
http://jsfiddle.net/6TSuq/1/
When you call $(page) you construct a jQuery object which contains 4 elements; for each of the HTMLElements that aren't nested in your string.
console.log($(page).map(function () {
return this.nodeName;
}).toArray());
// ["H1", "P", "SPAN", "P"]
​
html() returns the innerHTML of the first element in this jQuery object; which is why you only see "Hello!".
To find the .username you should use the filter() method, which searches within the jQuery object for elements which match the given selector.
alert($(page).filter('.username').text()); // "this is ur name", kthx.
See your updated fiddle here; http://jsfiddle.net/6TSuq/15/
Bare in mind that in future, you might have nested elements such as this;
var page = "<h1><span class='username'>Foo</span></h1>";
In this circumstance, filter()ing for .username will yeild no results; as the jQuery object $(page) does not contain the .username element; it contains a h1, which has a descendant which is a .username. Therefore here you'd need to use;
alert($(page).find('.username').text());
For reference, see find(), filter() and map()
A very simple way to do this is:
var elem = $('<h1>Hello!</h1><p>FOO</p><span class="username">this is ur name</span><p>sample text</p>').filter('.username').get(0);
console.log($(elem).html()); // returns this is ur name
​jsFiddle example
You need to convert your string to a jQuery object first in order to use any jQuery method on it.

Javascript array not working as expected

I'm pretty new to js/jquery. For each checkbox with the ID of check$ (where $ is a sequential number), I want to toggle the class "agree" of the surrounding span that uses the same check$ (but as a class). I don't want to have to hard-code the list of matching checkboxes, as this may vary.
Here's my code. This function works as expected:
agree = function (checkbox, span) {
$(checkbox).change(function(){
$(span).toggleClass('agree');
});
};
This is what I'm trying to pass to the above function, which does not work:
$(function() {
var elemid = 'check',
checks = Array($('[id^='+elemid+']').length);
console.log(checks);
for (i=0; i < checks; i++) {
agree('#'+elemid+checks[i], "."+elemid+checks[i]);
}
});
console.log(checks) returns [undefined × 4]. The number of elements is correct, but I don't know why it's undefined, or whether that is even significant.
The following code works as expected, but as I say, I'd rather not have to specify every matched element:
$(function() {
var checks = ["check1", "check2", "check3", "check4"];
for (i=0; i < checks.length; i++) {
agree('#'+checks[i], "."+checks[i]);
}
});
Thanks.
Edit: Thanks to Jack, I was overlooking the most simple method. I added the same class to all checkboxes and spans, and solved the problem with this:
$('input.check').change(function(){
$(this).closest('span.check').toggleClass('agree');
});
I might be totally missing something, but I'm pretty sure you are just trying to attach a change handler to each checkbox. In this case you can give them all the same class. I'm also guessing at your html structure for the span.
For reference:
http://api.jquery.com/closest/
http://docs.jquery.com/Tutorials:How_jQuery_Works
$('.yourcheckboxclass').change(function(){ //grab all elements with this class and attach this change handler
$(this).closest('span').toggleClass('agree');
});
The reason that the array is full of undefined values, is that you are just getting the number of items in the jQuery object, and create an array with that size. The jQuery object is discarded.
Put the jQuery object in the variable instead:
var elemid = 'check', checks = $('[id^='+elemid+']');
checks.each(function(){
agree(this, "."+elemid+checks[i]);
});

jQuery .wrap() question

i'm having some problems with jQuery
$(document).ready(function() {
var foo = $("<div><h1>Bar</h1><p>Hi</p><h1>Baz</h1><p>bye</p></div>");
foo.filter("h1,h2").map(function(id) {
$(this).wrap('<span color="red"/>');
});
alert(foo.html());
});
This code outputs
<h1>Bar</h1><p>Hi</p><h1>Baz</h2><p>bye</p>
The span's are nowhere to be seen. What am I doing wrong?
It doesn't have any effect because .filter() filters elements at that level, you could need .find() to get descendants like this:
$(document).ready(function() {
var foo = $("<div><h1>Bar</h1><p>Hi</p><h1>Baz</h1><p>bye</p></div>");
foo.find("h1,h2").wrap('<span color="red"/>');
alert(foo.html());
});
You can test it out here. Also note you should use .each() instead of .map() for looping...but there's no need here, since you can just call .wrap() directly.
You don't want to use filter here, you want to use find. Also, why are you using map?
$(document).ready(function() {
var foo = $("<div><h1>Bar</h1><p>Hi</p><h1>Baz</h2><p>bye</p></div>");
foo.find("h1,h2").wrap('<span color="red"/>');
alert(foo.html());
});
Live test
First off: your markup is invalid (Baz is wrapped by an opening h1 and a closing h2). But the .map reference says you need to return the value.
$(document).ready(function() {
var foo = $("<div><h1>Bar</h1><p>Hi</p><h1>Baz</h1><p>bye</p></div>");
var bar = foo.find("h1,h2").map(function(id) {
return $(this).wrap('<span color="red"/>');
});
});
You need .find() instead of .filter() since the heading elements are nested.
var foo = $("<div><h1>Bar</h1><p>Hi</p><h1>Baz</h1><p>bye</p></div>");
foo.find("h1,h2").wrap('<div color="red"/>');
Also, I changed it to wrap using a <div> instead of a <span> since I don't think it is valid to have a <span> wrapped around heading elements.

Categories

Resources