Following is the code where I display matched user input in the div but I want to hide the div when there is no match for user input. I can't seem to do it with the following code:
HTML code:
<input id="filter" type="text" placeholder="Enter your filter text here.." onkeyup = "test()" />
<div id="lc"> <p id='placeholder'> </p> </div>
JS code:
// JavaScript Document
s1= new String()
s2= new String()
var myArray = new Array();
myArray[0] = "Football";
myArray[1] = "Baseball";
myArray[2] = "Cricket";
myArray[3] = "Hockey";
myArray[4] = "Basketball";
myArray[5] = "Shooting";
function test()
{
s1 = document.getElementById('filter').value;
var myRegex = new RegExp((s1),"ig");
arraysearch(myRegex);
}
function arraysearch(myRegex)
{
document.getElementById('placeholder').innerHTML="";
for(i=0; i<myArray.length; i++)
{
if (myArray[i].match(myRegex))
{
document.getElementById('lc').style.visibility='visible';
document.getElementById('placeholder').innerHTML += myArray[i] + "<br/>";
}
else
{
document.getElementById('lc').style.visibility='hidden';
}
}
}
Regular expressions are a powerful tool but using them for so trivial a job is often troublesome.First you are using a direct input as regular expression which is never so good.
I copied your code and analyzed the logic you are making many many errors
for(i=0; i<myArray.length; i++)
{
if (myArray[i].match(myRegex))
{
document.getElementById('lc').style.visibility='visible';
document.getElementById('placeholder').innerHTML += myArray[i] + "<br/>";
}
else
{
document.getElementById('lc').style.visibility='hidden';
}
consider your code above, if I enter football, it matches with football, and football is shown. Next it checks for baseball which does not match and visibility changes to hidden!!
Better logic
1.Check what strings match, and add them to the division.
2.Check how many strings have matched, if none, change visibility to hidden.
You are using regular expressions when this actully can be achieved easily with indexOf();
these are pure logical errors
consider using jquery. (with a little http://underscorejs.org/ for utility)
var myArray = ["Football", "Baseball", "Cricket","Hockey", "Basketball", "Shooting"]
$("#filter").keyup(function() {
if(_.include(myArray, $(this).val()) {
$('#lc').show()
} else {
$('#lc').hide()
}
}
Related
I'm using a search-function for a documentation site which upon selection of search hit shows page with text highlighted (just as a pdf-reader or netbeans would do).
To achive the highlight i use javascript with:
function searchHighlight(searchTxt) {
var target = $('#page').html();
var re = new RegExp(searchTxt, 'gi');
target = target.replace(
re,
'<span class="high">' + searchTxt + '</span>'
);
$('#page').html(target);
}
Problem / Question:
Since page incudes images with filenames based on md5, some searches messes up the image src.
Searching on "1000" will distort the
<img src="53451000abababababa---.jpg"
to
<img src="5334<span class="hl">1000</span>abababab--.jpg">
Is it possible to solve this with regexp, somehow excluding anything anjcent to ".jpg"?
Or would it be possible to, before highligting replace the images with placeholders, and after replace revert back to src?
Example:
replace all <img *> with {{I-01}}, {{I-02}} etc and keep the real src in a var.
Do the replace above.
Revert back from {{I-01}} to the <img src=".."/>
DOM-manipulation is of course an option, but I figure this could be done with regexp somehow, however, my regexp skills are lacking badly.
UPDATE
This code works for me now:
function searchHighlight(searchTxt) {
var stack = new Array();
var stackPtr = 0;
var target = $('#page').html();
//pre
target = target.replace(/<img.+?>/gi,function(match) {
stack[stackPtr] = match;
return '{{im' + (stackPtr++) + '}}';
});
//replace
var re = new RegExp(searchTxt, 'gi');
target = target.replace(re,'<span class="high">' + searchTxt + '</span>');
//post
stackPtr = 0;
target = target.replace(/{{im.+?}}/gi,function(match) {
return stack[stackPtr++];
});
$('#page').html(target);
}
One approach would be to create an array of all possible valid search terms. Set the terms as .textContent of <span> elements within #page parent element.
At searchHighlight function check if searchTxt matches an element within array. If searchTxt matches an element of array, select span element using index of matched array element, toggle "high" .className at matched #page span element, else notify user that searchTxt does not match any valid search terms.
$(function() {
var words = [];
var input = $("input[type=text]");
var button = $("input[type=button][value=Search]");
var reset = $("input[type=button][value=Reset]");
var label = $("label");
var page = $("#page");
var contents = $("h1, p", page).contents()
.filter(function() {
return this.nodeType === 3 && /\w+/.test(this.nodeValue)
}).map(function(i, text) {
var span = text.nodeValue.split(/\s/).filter(Boolean)
.map(function(word, index) {
words.push(word);
return "<span>" + word + "</span> "
});
$(text.parentElement).find(text).replaceWith(span);
})
var spans = $("span", page);
button.on("click", function(event) {
spans.removeClass("high");
label.html("");
if (input.val().length && /\w+/.test(input.val())) {
var terms = input.val().match(/\w+/g);
var indexes = $.map(terms, function(term) {
var search = $.map(words, function(word, index) {
return word.toLowerCase().indexOf(term.toLowerCase()) > -1 && index
}).filter(Boolean);
return search
});
if (indexes.length) {
$.each(indexes, function(_, index) {
spans.eq(index).addClass("high")
})
} else {
label.html("Search term <em>" + input.val() + "</em> not found.");
}
}
});
reset.on("click", function(event) {
spans.removeClass("high");
input.val("");
label.html("");
})
})
.high {
background-color: #caf;
}
label em {
font-weight: bold;
background-color: darkorange;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input type="text" />
<input type="button" value="Search" />
<input type="button" value="Reset" />
<label></label>
<div id="page" style="max-width:500px;border:1px solid #ccc;">
<h1 style="margin:0px;">test of replace</h1>
<p>After Luke comes to Dagobah, Yoda initially withholds his true identity. He’s trying to get a sense of who Luke is as a person; Yoda understands that there’s a lot at risk in training Luke to be a Jedi, especially considering what happened with his
father.
<img style="float:right;" width="200" src="http://a.dilcdn.com/bl/wp-content/uploads/sites/6/2013/11/04-400x225.jpg">And Yoda is not impressed — Luke is impatient and selfish. With “Adventure. Excitement. A Jedi craves not these things,” the Jedi Master makes clear that Luke must understand the significance and meaning of the journey he thinks he wants to make.
It’s an important lesson for Luke and for audiences, because when Luke faces Vader at the film’s climax, we see the stakes involved in the life of a Jedi</p>
<p>Now Yoda-search works, however a search on "sites" will break the image-link. (Yes, I know this implementation isn't perfect but I'm dealing with reality)</p>
</div>
I was hoping someone can help me figure out what I'm doing wrong... There's this exercise in the Eloquent JS book that asks you to write some code that can suggest words/values to users as they type... The code I've written is below. What happens is that when I run the code, the div element's text content changes to the wrong value. Specifically, it's set to a string of all the elements inside the array 'terms'. I really cant figure out why this happens!
<input type="text" id="field">
<div id="suggestions" style="cursor: pointer"></div>
<script>
// Builds up an array with global variable names, like
// 'alert', 'document', and 'scrollTo'
var terms = [];
for (var name in window)
terms.push(name);
var input = document.querySelector('input');
var div = document.querySelector('#suggestions');
input.addEventListener("input", function(event){
var last = input.value.lastIndexOf(" ")+1;
var check = input.value.slice(last);
var reg = new RegExp(check);
for (var i=0; i<terms.length; i++) {
if (reg.test(terms[i])) {
var text = document.createTextNode(terms[i]);
div.appendChild(text)};
};
})
</script>
I guess you forgot to clean the div before each change in the input.
I also added a space after each word to make the output more readable.
// Builds up an array with global variable names, like
// 'alert', 'document', and 'scrollTo'
var terms = [];
for (var name in window)
terms.push(name);
var input = document.querySelector('input');
var div = document.querySelector('#suggestions');
input.addEventListener("input", function(event){
div.innerHTML = '';
var last = input.value.lastIndexOf(" ")+1;
var check = input.value.slice(last);
var reg = new RegExp(check);
for (var i=0; i<terms.length; i++) {
if (reg.test(terms[i])) {
var text = document.createTextNode(terms[i] + ' ');
div.appendChild(text)};
};
})
<input type="text" id="field">
<div id="suggestions" style="cursor: pointer"></div>
With this code, you will display the name of the properties from the window object that contains the last word from the input. Try it writting "window location document". Is it what you are looking for?
I have a list with about 10 000 customers on a web page and need to be able to search within this list for matching input. It works with some delay and I'm looking for the ways how to improve performance. Here is simplified example of HTML and JavaScript I use:
<input id="filter" type="text" />
<input id="search" type="button" value="Search" />
<div id="customers">
<div class='customer-wrapper'>
<div class='customer-info'>
...
</div>
</div>
...
</div>
<script type="text/javascript">
$(document).ready(function() {
$("#search").on("click", function() {
var filter = $("#filter").val().trim().toLowerCase();
FilterCustomers(filter);
});
});
function FilterCustomers(filter) {
if (filter == "") {
$(".customer-wrapper").show();
return;
}
$(".customer-info").each(function() {
if ($(this).html().toLowerCase().indexOf(filter) >= 0) {
$(this).parent().show();
} else {
$(this).parent().hide();
}
});
}
</script>
The problem is that when I click on Search button, there is a quite long delay until I get list with matched results. Are there some better ways to filter list?
1) DOM manipulation is usually slow, especially when you're appending new elements. Put all your html into a variable and append it, that results in one DOM operation and is much faster than do it for each element
function LoadCustomers() {
var count = 10000;
var customerHtml = "";
for (var i = 0; i < count; i++) {
var name = GetRandomName() + " " + GetRandomName();
customerHtml += "<div class='customer-info'>" + name + "</div>";
}
$("#customers").append(customerHtml);
}
2) jQuery.each() is slow, use for loop instead
function FilterCustomers(filter) {
var customers = $('.customer-info').get();
var length = customers.length;
var customer = null;
var i = 0;
var applyFilter = false;
if (filter.length > 0) {
applyFilter = true;
}
for (i; i < length; i++) {
customer = customers[i];
if (applyFilter && customer.innerHTML.toLowerCase().indexOf(filter) < 0) {
$(customer).addClass('hidden');
} else {
$(customer).removeClass('hidden');
}
}
}
Example: http://jsfiddle.net/29ubpjgk/
Thanks to all your answers and comments, I've come at least to solution with satisfied results of performance. I've cleaned up redundant wrappers and made grouped showing/hiding of elements in a list instead of doing separately for each element. Here is how filtering looks now:
function FilterCustomers(filter) {
if (filter == "") {
$(".customer-info").show();
} else {
$(".customer-info").hide();
$(".customer-info").removeClass("visible");
$(".customer-info").each(function() {
if ($(this).html().toLowerCase().indexOf(filter) >= 0) {
$(this).addClass("visible");
}
});
$(".customer-info.visible").show();
}
}
And an test example http://jsfiddle.net/vtds899r/
The problem is that you are iterating the records, and having 10000 it can be very slow, so my suggestion is to change slightly the structure, so you won't have to iterate:
Define all the css features of the list on customer-wrapper
class and make it the parent div of all the list elements.
When your ajax request add an element, create a variable containing the name replacing spaces for underscores, let's call it underscore_name.
Add the name to the list as:
var customerHtml = "<div id='"+underscore_name+'>" + name + "</div>";
Each element of the list will have an unique id that will be "almost" the same as the name, and all the elements of the list will be on the same level under customer-wrapper class.
For the search you can take the user input replace spaces for underscores and put in in a variable, for example searchable_id, and using Jquery:
$('#'+searchable_id).siblings().hide();
siblings will hide the other elements on the same level as searchable_id.
The only problem that it could have is if there is a case of two or more repeated names, because it will try to create two or more divs with the same id.
You can check a simple implementation on http://jsfiddle.net/mqpsppxm/
What is the most convenient way of injecting a number into the HTML of the site (using Chrome Extensions), when the given parameter is found in the website's code? For example we have a list:
www.newsweek.com, hf-title, 2
www.aaa.com, yzs, 1
www.ccc.com, abc, 123
When we find "hf-title" on the website www.newseek.com then number "2" is inserted next to the found paragraph on the website in the browser. When we find "abc" in the code of the website www.ccc.com then number "123" is inserted next to the table, and so on.
There cannot be any connection to the database, just javascript.
The list that is going to be used will be hundreds of rows long, so it is really problematic to use switch statement.
The source table has to be located in the Google Chrome extension files on the PC. The information should be looked for when (or shortly after) the site is being opened.
Example of the source code:
<h2 class="hf-title">
Four NATO Allies Deny Ukraine<span class="overlay article-overlay"></span>
</h2>
<div class="hf-summary">
NATO officials have previously said... </div>
</div>
We add simply
<a> 2 </a>
at the end.
Any ideas? ;)
What you will likely need to do is find all of the text nodes on the page. From there you can begin editing them. The 'modifyTextNodes' function is an example of using a TreeWalker to do this. It is a very efficient method for traversing the DOM.
var arr = [{url:"www.newsweek.com", string:"hf-title", value:"2"},
{url:"www.aaa.com", string:"yzs", value:"1"},
{url:"www.ccc.com", string:"abc", value:"123"}];
function modifyTextNodes() {
var el = document.getElementsByTagName('html')[0];
var walk=document.createTreeWalker(el,NodeFilter.SHOW_TEXT,null,false);
while(n = walk.nextNode()) {
modifyNode(n);
}
}
function modifyNode(node) {
if (node.nodeType == Node.TEXT_NODE && node.parentNode != undefined && (val = node.nodeValue.trim())) {
var addToEnd = "";
for (var i=0; i<arr.length; i++) {
if (document.baseURI.indexOf(arr[i].url) > -1 && val.indexOf(arr[i].string) > -1) {
addToEnd += arr[i].value;
}
}
}
if (addToEnd) {
node.nodeValue += addToEnd;
}
}
Alternatively, if it is elements that you are trying to find, you could use querySelectorAll to find all the matching elements.
document.querySelectorAll("[class='" + arr[i].string + "']");
In this case 'modifyAllNodes' would look like
function modifyAllNodes() {
for (var i = 0; i<arr.length; i++) {
if (document.baseURI.indexOf(arr[i].url) > -1) {
var nodes = document.querySelectorAll("[class='" + arr[i].string + "']");
modifyNodes(nodes, arr[i]);
}
}
}
function modifyNodes(nodes, arrEl) {
for (var i=0; i<nodes.length; i++) {
if (node.nodeValue.indexOf(arrEl.string) > -1) {
node.nodeValue += arrEl.value;
}
}
}
first you have to know the structure of the list you are trying to "hack", which means the ID or class names. Afterwards, in your JS check each record of that list if its content matches the string you pass to and then do a .append()
I'm using the below in a javascript Q & A chatbot. To answer for example "what is AG in the periodic table? Answer is Silver.
if ((input.search("(what is|what's)") != -1) && (input.search("(periodic table)") != -1)) {
document.result.result.value = "Hmmmm, I don't know. Try Google!";
for (i = 0; i < Periodic_Tables.length; i++) {
Periodic_Table = Periodic_Tables[i].split('=');
if (input.indexOf(Periodic_Table[0]) != -1) {
document.result.result.value = Periodic_Table[1];
}
}
return true;
}
Then I have in another file the array laid out like this:
Periodic_Tables=new Array(
"h=Hydrogen",
"he=Helium",
"li=Lithium",
"be=Beryllium",
"b=Boron",
"c=Carbon",
"n=Nitrogen",
"o=Oxygen",
"f=Fluorine",
"ne=Neon",
"na=Sodium",
"mg=Magnesium",
"ag=Silver"
);
My problem is because the table symbols are only 1 or 2 letters it's matching a lot of wrong things. How can I set this up where "only" b matches boron, be matches beryllium. etc I've looked a word boundaries but can seem to figure out how to use them here.
Instead of this code block which is checking if input contains a symbol:
if (input.indexOf(Periodic_Table[0]) != -1) {
document.result.result.value = Periodic_Table[1];
}
You should check for equality like this:
Periodic_Tables=new Array("h=Hydrogen",
"he=Helium", "li=Lithium", "be=Beryllium", "b=Boron", "c=Carbon", "o=Oxygen",
"f=Fluorine", "ne=Neon", "na=Sodium", "mg=Magnesium", "ag=Silver");
function Parse(input) {
input=input.toLowerCase();
input=input.replace(/[!|?|,|.]/g,"");
if (input.search(/\bu\b/)!=-1) input=input.replace(/\bu\b/,"you");
if (input.search(/\br\b/)!=-1) input=input.replace(/\br\b/,"are");
if (input.search(/\bk\b/)!=-1) input=input.replace(/\bk\b/,"ok");
if (input.search(/\by\b/)!=-1) input=input.replace(/\by\b/,"why");
var words=input.split(" ");
var result = "Hmmmm, I don't know. Try Google!";
if ((input.search("(what is|what's)") != -1) && (input.search("(periodic table)") != -1)) {
for (var w=0; w<words.length; w++) {
for (i=0; i<Periodic_Tables.length; i++) {
Periodic_Table = Periodic_Tables[i].split('=');
if (words[w] == Periodic_Table[0]) {
result = Periodic_Table[1];
return result;
}
}
}
}
return result;
}
alert(Parse("what is h in periodic table"));
DEMO: http://jsfiddle.net/MnyFP/1/
First i'd use 2d array to store your periodic tables, so that you don't have to string split every time you want to use it.
Instead of
Periodic_Tables=new Array(
"h=Hydrogen",
"he=Helium",
"li=Lithium",
"be=Beryllium",
"b=Boron",
"c=Carbon",
"n=Nitrogen",
"o=Oxygen",
"f=Fluorine",
"ne=Neon",
"na=Sodium",
"mg=Magnesium",
"ag=Silver",
);
Use
Periodic_Tables = [
["h", "Hydrogen"],
["he", "Helium"],
...
]
Assuming that the question is well formatted, that the symbol "AG" has a space in front and after it. I think you could simple test the input against " AG ", or " ag ", if you make it case insensitive. Including the spaces in the test string will for it to find matches that is a word in it self, instead of part of another word.
Pretty use regex has similar abilities, but i am not sure how to do it with regex..
assuming that the question in the chat box ends with the name of the element, u can split the string at the punctuations.(lets say user enters, what is,ag)
function abc(str)
{
String[] parts = str.split("\\W+");
var len=parts.length();
String sub=parts[len-1];
var re=new RegExp("^"+sub+"$","i");
and then use a loop and check the condition
if(re.test(arr[i])){
document.write(arr[i]);
break;
}
}
I would use an array of objects:
Periodic_Tables = [
{Symbol: "h", Element: "Hydrogen"},
{Symbol: "he", Element: "Helium"}
...
]
Then your loop looks like this:
for (i = 0; i < Periodic_Table.length; i++) {
if (input.indexOf(Periodic_Table[i].Symbol) !== -1) {
document.result.result.value = Periodic_Table[i].Element;
}
}
This prevents having to use a regex or a 2d array, and is a little more readable.
I can't seem to get anything to work within the Q and A bot. So I put up a demo here:
http://www.frontiernet.net/~wcowart/aademo.html
Or here is the code: I tried many of the various answers presented. Maybe I'm not implementing them right.
<HTML>
<HEAD>
<TITLE>ChatterBot Page</TITLE>
<script language="JavaScript">
Periodic_Tables=new Array(
"h=Hydrogen",
"he=Helium",
"li=Lithium",
"be=Beryllium",
"b=Boron",
"c=Carbon",
"n=Nitrogen",
"o=Oxygen",
"f=Fluorine",
"ne=Neon",
"na=Sodium",
"mg=Magnesium",
"ag=Silver"
);
var message = new Array();
var randomnum;
var flagrandom;
function Parse() {
var input = new String(document.chat.input.value);
document.chat.input.value="";
input=input.toLowerCase();
word=input.split(" ");
num_of_words=word.length;
input=input.replace(/[!|?|,|.]/g,"");
word=input.split(" ");
if (input.search(/\bu\b/)!=-1) input=input.replace(/\bu\b/,"you");
if (input.search(/\br\b/)!=-1) input=input.replace(/\br\b/,"are");
if (input.search(/\bk\b/)!=-1) input=input.replace(/\bk\b/,"ok");
if (input.search(/\by\b/)!=-1) input=input.replace(/\by\b/,"why");
if ((input.search("(what is|what's)") != -1) && (input.search("(periodic table)") != -1)) {
document.result.result.value = "Hmmmm, I don't know. Try Google!";
for (var i = 0, len = Periodic_Tables.length; i < len; i++) {
if (Periodic_Tables[i].match('^'+input+'=')) {
document.result.result.value = Periodic_Tables[i].split('=')[1] }
}
return true;}
if (!flagrandom) {
randomnum = [Math.floor(Math.random()*3)]
flagrandom=true;}
message[0] = "Sorry, you stumped me on that one.";
message[1] = "Sorry, a search of my data base comes up empty.";
message[2] = "Not sure";
document.result.result.value = message[randomnum];
randomnum++
if (randomnum>2){randomnum=0}
return true;}
</script>
</head>
<BODY BACKGROUND="FFFFFF" TEXT="#0000cc" LINK="#FFCOOO" ALINK="#FFCC99"
VLINK="#FFC000" marginwidth="0" leftmargin="0" topmargin="0" rightmargin="0">
<Center>
<font size="+3">
ChatterBot
</font>
<br>
<img src="botpix.jpg" border="0" WIDTH="200" HEIGHT="200">
<br>
<form name="result">
<textarea rows=5 cols=40 input type="text" name="result">
</textarea><br>
</form>
</center>
<form name="chat" onSubmit="Parse();return false">
<b>Type here:</b>
<input type="text" name="input" size="100">
</form>
</body>
</html>