In Javascipt (or better in jQuery), how do I check if a given string is contained in an html content of a <p> and <span> (ORed search)? Example:
<p id="p1">apple boy cat</p>
<p id="p2">ant boy cow</p>
<p id="p3">axe boots cat</p>
<span id="sp1">boots</span>
<span id="sp2">cow</span>
<span id="sp3">ant</span>
Search string: "apple boots cat"
Output:
p1, p3, sp1
var searchArray = 'apple boots cat'.split(' ');
var found = $('p, span').filter(function(idx, elem) {
var html = $(elem).html();
for(var i = 0, l = searchArray.length; i < l; i++) {
if(html.indexOf(searchArray[i]) != -1) {
return true;
}
}
return false;
}).css('color', '#f00');
Demo: http://jsfiddle.net/ThiefMaster/sWd2t/
var phrases = "apple boots cat".split(/\s+/);
$("*").filter(function () {
var found = false;
var $this = $(this);
$.each(phrases, function (phrase) {
if ($this.text().search(phrase) !== -1) {
found = true;
return false; // break the `each`
}
});
return found;
});
This is untested, but you get the idea. Select the elements which you want to search through and then use filter to narrow it down. The initial selector will have a large influence on the speed of this, and you'll also get multiple matches if there are nested elements. Without knowing your circumstances it's hard to recommend anything though - just be aware of these things. Hopefully this is a start.
var words = new RegExp("apple|boots|cat"); // looking for "apple", "boots" and "cat"
var output = $('p, span').filter(function() { // search "p" and "span"
return words.test($(this).text());
}).map(function() {
return $(this).attr('id'); // return the value of "id" from the found nodes
});
Note that the search string uses | instead of space to separate the words. Just do a replace on all the spaces if that's a problem.
This is a slightly convoluted demo, but: given a slightly adapted html:
<form action="#" method="post">
<fieldset>
<input placeholder="Search for string" type="search" id="search" name="search" />
</fieldset>
</form>
<div id="results">0 results.</div>
<div id="wrap">
<p id="p1">apple boy cat</p>
<p id="p2">ant boy cow</p>
<p id="p3">axe boots cat</p>
<span id="sp1">boots</span>
<span id="sp2">cow</span>
<span id="sp3">ant</span>
</div>
And the jQuery:
$('#search').keypress(
function(k) {
var string = $(this).val();
$('.highlight').removeClass('highlight');
$('#wrap').children().filter(':contains(' + string + ')').addClass('highlight');
$('#results').text($('.highlight').length + ' results.');
if (k.which === 13) {
return false;
}
});
JS Fiddle demo, this can give in-page searching options.
Related
I want to detect a specific word or multiple words within the user's entered text and reply accordingly. I plan to add more words to detect but for now I've been using this.
My result is finalKey.contains is not a function.
<html>
<div>
<p1 id="iOut">🧰</p1>
</div>
<div>
<input id="uIn" value=""></input>
</div>
<button onclick="regis()">SUBMIT</button>
<script>
var key = document.getElementById("uIn").value;
var finalKey = key.toUpperCase();
function regis() {
if (finalKey.contains("Hi" || "H")) {
document.getElementById("iOut").innerHTML = "HEY";
} else if (finalKey.contains("Bye" || "Goodbye")) {
document.getElementById("iOut").innerHTML = "Okay";
} else {
document.getElementById("iOut").innerHTML = "🧰 Try again";
}
}
</script>
</html>
There is no such thing as contains. It is .includes or indexOf != -1
Your gathering of values needs to be inside the function too
Also you cannot test two values in one statement unless you turn it around and use an array:
["Hi","H"].indexOf(finalKey) !=-1
or
["HI","H"].filter(text => finalKey.startsWith(text)).length > 0
if you want finalkey to start with either - use .includes if you want to test the complete input
Lastly you uppercased the text so compare uppercase text
function regis() {
var key = document.getElementById("uIn").value;
var finalKey = key.toUpperCase();
if (["HI","H"].filter(text => finalKey.includes(text)).length > 0) {
document.getElementById("iOut").innerHTML = "HEY";
} else
if (["BYE","GOODBYE"].filter(text => finalKey.includes(text)).length > 0) {
document.getElementById("iOut").innerHTML = "Okay";
} else // GOOD has to be AFTER GOODBYE to not catch it
if (["GOOD","GREAT"].filter(text => finalKey.includes(text)).length > 0) {
document.getElementById("iOut").innerHTML = "That's Good";
} else {
document.getElementById("iOut").innerHTML = "🧰 Try again";
}
}
<div>
<p1 id="iOut">🧰</p1>
</div>
<div>
<input id="uIn" value="" />
</div>
<button type="button" onclick="regis()">SUBMIT</button>
Using regular expression and word boundaries
Note I wrapped in a form, now you can just hit enter too
const wordList = [
{ list: ["HI", "H"], answer: "HEY" },
{ list: ["BYE", "GOODBYE"], answer: "Okay" },
{ list: ["GOOD", "GREAT"], answer: "That's good" }
];
const defaultText = "🧰 Try again";
document.getElementById("inputForm").addEventListener("submit", e => {
e.preventDefault()
const input = document.getElementById("uIn").value.trim().toUpperCase();
let result = wordList
.filter(({ list, answer }) => list
.filter(word => new RegExp("\\b" + word + "\\b").test(input))
.length > 0);
console.log(result)
document.getElementById("iOut").innerHTML = result.length > 0 ? result[0].answer : defaultText;
})
<div>
<p1 id="iOut">🧰</p1>
</div>
<form id="inputForm">
<div>
<input id="uIn" value="" />
</div>
<button>SUBMIT</button>
</form>
You will want to use .includes() and not .contains().
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String
I'd recommend getting comfortable with Chrome's Developer Tools. For example, pull up a browser and enter "xyz". You will see various methods available, and contains() is not on the list.
Also, as #mplungjan pointed out, there are other problems here.
"Hi" || "H" evaluates to "Hi". So "HI" is entirely ignored here.
You could write finalKey.includes("Hi") || finalKey.includes("H")) instead, but this would get cumbersome as you add other conditions.
A better approach would be to use functional programming along these lines:
const wordsToTest = ['FOO', 'BAR'];
if (wordsToTest.find(word => finalKey.includes(word))) {
I was made aware that i made a fundemental mistake in my previous answer, so i came up with another solution. With this approach you will not have to make multiple if/else statements, however simply add new object to the array, i hope it is pretty self explanatory when looking at it :)
<html>
<div>
<p1 id="iOut">🧰</p1>
</div>
<div>
<input id="uIn" value=""></input>
</div>
<button onclick="regis()">SUBMIT</button>
<script>
function regis() {
let key = document.getElementById("uIn").value;
let finalKey = key.toUpperCase();
let match = false;
// Words to test for
autoComplete = [
{ // Hey
response: "hey",
input: [
'HI',
'H',
]
},
{ // Bye
response: "Bye",
input: [
'BYE',
'GOODBYE',
]
}
]
for (potentialMatch of autoComplete){
for (input of potentialMatch.input){
if (input === finalKey){
document.getElementById("iOut").innerHTML = potentialMatch.response;
match = true;
}
}
}
if (match === false)
document.getElementById("iOut").innerHTML = "🧰 Try again";
}
</script>
</html>
I'm trying to compare a input value with two paragrapah to check if the input value exists in both paragraph. So, I did this below. But the code is not working well :/. Could someone explain how to do it?
<input type="text" class="input-text" name="billing_district" id="billing_district" placeholder="" value="">
<p class="regions">Dias Macedo, Itaperi, Passaré, Taquara, Serrinha</p>
<p class="regions">Dias Macedo, Dendê, Edson Queiroz, Taquara</p>
<p class="regions">Jereissati, Dendê, Forquilha, Centro, Taquara</p>
jQuery(function ($) {
var a = $('#billing_district').val().normalize('NFD').replace(/[\u0300-\u036f]/g, '').toLowerCase().split();
var b = $('.regions').text().normalize('NFD').replace(/[\u0300-\u036f]/g, '').toLowerCase().split(", ");
var index = $.grep(b, function (element, index) {
if ($.inArray(element, a) != -1) {
console.log(element);
}
});
});
This works, though you did not specify that the code should look at whole terms between commas. This code outputs true even if two letters occur in all the p's.
But you could add an extra loop to check the splitted strings.
jQuery(function($) {
const $input = $('#billing_district');
const b = $('.regions');
$('#billing_district').on('keyup', function(){
let a = $input.val();
let count = 0
$.each(b, function(i, p) {
console.log($(p).text().replace(/\s/g, ""),a);
if ($(p).text().replace(/\s/g, "").includes(a)) {
count++;
}
});
let valueIsInAllParagraphs = (count == 3);
console.log(valueIsInAllParagraphs);
});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<input type="text" class="input-text" name="billing_district" id="billing_district" placeholder="" value="">
<p class="regions">Dias Macedo, Itaperi, Passaré, Taquara, Serrinha</p>
<p class="regions">Dias Macedo, Dendê, Edson Queiroz, Taquara</p>
<p class="regions">Jereissati, Dendê, Forquilha, Centro, Taquara</p>
I need to convert top-level newlines in a label to <br> so that they actually look like newlines.
E.g. the label
<label>Hello
there<div>who
knows</div>what's going on</label>
should become
<label>Hello<br>there<div>who
knows</div>what's going on</label>
I tried already scanning the top-level text nodes using .childNodes and replacing the text, but that leaves a <br> in text output, as in, it doesn't get formatted correctly and is just outputted to text that the user can see.
$("label[for^='answer']").each(function () {
var thisElement = this;
Array.prototype.slice.call($(thisElement)[0].childNodes).forEach(function (elem, index) {
if (typeof elem.nodeValue == 'string') {
$(thisElement)[0].childNodes[index].nodeValue = $(thisElement)[0].childNodes[index].nodeValue.replace(/\r\n/g, '<br>').replace(/\n/g, '<br>');
};
});
});
How can I replace all top-level newlines with correct HTML to make them look like newlines in the output?
You can't simply replace \n inside <label> because it will add <br>s to other elements, such as the <div> inside the <label> on your example.
You will have to iterate over the text nodes and create/insert <br> element in their places.
Also, to identify the text nodes, don't use
if (typeof elem.nodeValue == 'string') {
Use:
if (elem.nodeType === Node.TEXT_NODE) {
Furthermore, see demo below.
//$("label[for^='answer']").each(function () {
$("label.change-me").each(function () {
var thisElement = this;
Array.prototype.slice.call(thisElement.childNodes).forEach(function (elem, index) {
if (elem.nodeType === Node.TEXT_NODE) {
var lines = elem.nodeValue.split('\n');
if (lines.length > 1) { // i.e. there are line breaks
elem.nodeValue = lines[0];
var nextElement = elem.nextSibling;
for (var i = 1; i < lines.length; i++) {
elem.parentNode.insertBefore(document.createElement('br'), nextElement);
elem.parentNode.insertBefore(document.createTextNode(lines[i]), nextElement);
}
}
};
});
});
label { color: red }
div { color: blue }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<label class="change-me">Hello
there<div>who
knows</div>what's going on</label>
<hr>
<label>Hello<br>there<div>who
knows</div>what's going on</label>
<hr>
<label class="change-me">Line-breaks inside
labels
break<div>I'm
inside
div
so
don't
break
me</div>div is over so break
me because we are
back to the label</label>
What i understood by you question, you want to replace the first space with a <br> tag.
So i added the splice() function to a string to solve this
Hope this is what you were looking for. Happy to explain or help in a better solution if needed.
String.prototype.splice = function(idx, rem, str) {
return this.slice(0, idx) + str + this.slice(idx + Math.abs(rem));
};
$("label").each(function() {
var thisText = this.innerHTML;
var result = thisText.splice(thisText.indexOf(" "), 0, "<br>");
this.innerHTML = result;
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<label>
Hello there
<div>who knows</div>
what's going on
</label>
So here is my current js fiddle
http://jsfiddle.net/sbyLb93n/6/
I have tried adding
.replace(/\W*/mg, "")
to wordsArr and valuesArr before I turn the string of text into an array of strings with .split(" ") (so right after i use .trim() on valuesArr and .toLowerCase() on wordsArr). should I iterate through the array after splitting and say 'for each arr in wordsArr, if arr has a character that isn't in a-z or 0-9, then remove that character and return this new string" ? if so, from where should i start to achieve this loop through the arrays? thanks dogs!
You do this using the filterCallback option of the filterable widget. Basically, override the default filter to compare the search text against the text with all punctuation stripped out:
<form class="ui-filterable">
<input id="myFilter" data-type="search"></input>
</form>
<div class="elements" data-filter="true" data-input="#myFilter">
<p class="parag">This is paragraph one or 1; it's not number 2</p>
<p class="parag">Next is two too 2.</p>
<p class="parag">Three 3! is after 2</p>
<p class="parag">Finally is, 4 four but; 'not" two.</p>
</div>
$(document).on("pagecreate", "#page1", function()
$(".elements").filterable('option', 'filterCallback', NoPunctuation);
});
function NoPunctuation( idx, searchValue ) {
var ret = false;
if (searchValue && searchValue.length > 0){
var filttext = $(this).data("filtertext") || '';
filttext = filttext.toLowerCase();
var text = $(this).text().toLowerCase();
var punctRE = /[\u2000-\u206F\u2E00-\u2E7F\\'!"#\$%&\(\)\*\+,\-\.\/:;<=>\?#\[\]\^_`\{\|\}~]/g;
var spaceRE = /\s+/g;
text = text.replace(punctRE, '').replace(spaceRE, ' ');
console.log(text);
if( text.indexOf(searchValue.toLowerCase()) < 0 && filttext.indexOf(searchValue.toLowerCase()) < 0){
ret = true; //filter this one out
}
}
return ret;
}
To strip punctuation, I used an answer from here: How can I strip all punctuation from a string in JavaScript using regex?
I'm building an icon library where the user on the front end (submitting a form) can select an icon. I managed to get everything working as far as the selection process. Now, the final product will have over 400 icons, and i wanted to add a search (ajax, i guess) or autocomplete input where the user can type a couple of letters and it filter's out those icons.
They search will be filtering out some with a class that has the prefix "icon-".
I started on jsFiddle here: http://jsfiddle.net/yQMvh/28/
an example would be something like this :
http://anthonybush.com/projects/jquery_fast_live_filter/demo/
My HTML Markup:
<div class="iconDisplay">Display's selected icon</div>
<span id="selectedIcon" class="selected-icon" style="display:none"></span>
<button id="selectIconButton">Select Icon</button>
<div id="iconSelector" class="icon-list">
<div id="iconSearch">
<label for="icon-search">Search Icon: </label>
<input type="text" name="icon-search" value="">
</div>
<span class="icon-icon1"></span>
<span class="icon-icon2"></span>
<span class="icon-icon3"></span>
<span class="icon-icon4"></span>
<span class="icon-icon5"></span>
<span class="icon-icon6"></span>
<span class="icon-icon7"></span>
<span class="icon-icon8"></span>
</div>
JS (note: this includes the selection jQuery as well):
var iconVal = $(".icon_field").val();
$('#selectedIcon').addClass(iconVal);
$("#selectIconButton").click(function () {
$("#iconSelector").fadeToggle();
});
$("#iconSelector span").click(function () {
selectIcon($(this));
});
function selectIcon(e) {
var selection = e.attr('class');
$(".icon_field").val(selection);
$("#iconSelector").hide();
$('#selectedIcon').removeClass();
$('#selectedIcon').addClass(selection).show();
return;
}
Will this work for you? http://jsfiddle.net/yQMvh/37/
I've modified your input field slightly (added an id)
<input type="text" id="txt-icon-search" name="icon-search" />
and added this bit of code.
/**
* Holds information about search. (document later)
*/
var search = {
val: '',
icons: function (e) {
// get all the icons.
var icons = $('span[class*="icon-"]');
// assign the search val. (can possibly use later)
search.val = $(e.currentTarget).val();
// let the looping begin!
for (var i = 0, l = icons.length; i < l; i++) {
// get the current element, class, and icon after "icon-"
var el = $(icons[i]),
clazz = el.attr('class'),
iconEnd = clazz.substr(5, clazz.length);
// was the value found within the list of icons?
// if found, show.
// if not found, hide.
(iconEnd.indexOf(search.val) === -1) ? el.hide() : el.show();
}
}
};
$('#txt-icon-search').keyup(search.icons);
One possible way could be to use DataTables, this framework includes a search functionality, its row based tho, could be modified probably. Or if you want to present each icon with some facts like size, name, creator, it would be good maybe. The user could then sort the height etc.
Have a look here
Its a bit heavy weight but have a lot of possibilities for optimization
What you're looking for is something like this: http://jqueryui.com/autocomplete/
Pretty easy and all ready to use. You could pre-populate the available tags with your icons selection. Quick example:
$(function() {
var availableTags = [
"icon-name1",
"icon-name2",
"icon-name3",
"etc."
];
$( "input[name=icon-search]" ).autocomplete({
source: availableTags
});
});
EDIT: of course you can do something much more sophisticated, like displaying a thumbnail/preview of your icon next to each result
EDIT2:
From the sample in your link, I quickly threw something together to have it the way you wanted it:
JSCODE:
<script>
$(function() {
$.expr[':'].Contains = function(a,i,m){
return ($(a).attr("data-index") || "").toUpperCase().indexOf(m[3].toUpperCase())>=0;
};
function listFilter(header, list) {
$("input.filterinput")
.change( function () {
var filter = $(this).val();
if(filter) {
$(list).find("span:not(:Contains(" + filter + "))").parent().slideUp();
$(list).find("span:Contains(" + filter + ")").parent().slideDown();
} else {
$(list).find("li").slideDown();
}
return false;
})
.keyup( function () {
$(this).change();
});
}
$(function () {
listFilter($("#iconSearch"), $("#list"));
});
});
</script>
Your html code tweaked a little:
<div id="iconSelector" class="icon-list" style="display: block;">
<div id="iconSearch">
<label for="icon-search">Search Icon: </label>
<input type="text" name="icon-search" class="filterinput" value="">
</div>
<ul id="list">
<li><span class="icon-icon1" data-index="red"></span></li>
<li><span class="icon-icon2" data-index="yellow"></span></li>
<li><span class="icon-icon3" data-index="blue"></span></li>
</ul>
</div>
Now if you type "red" you'll get the first span since the search is looking for a match from the data-index attribute. You can replace those with "Facebook", "Twitter", or whatever the name of your icon is.
If you want to directly search from the class name you can do something like this then:
<script>
$(function() {
$.expr[':'].Contains = function(a,i,m){
return ($(a).attr("class") || "").toUpperCase().indexOf(m[3].toUpperCase())>=0;
};
function listFilter(header, list) {
$("input.filterinput")
.change( function () {
var filter = "icon-" + $(this).val();
if(filter) {
$(list).find("span:not(:Contains(" + filter + "))").parent().slideUp();
$(list).find("span:Contains(" + filter + ")").parent().slideDown();
} else {
$(list).find("li").slideDown();
}
return false;
})
.keyup( function () {
$(this).change();
});
}
$(function () {
listFilter($("#iconSearch"), $("#list"));
});
});
</script>