Hey All I have this javascript function that actively searches a table I have on my web page. The function working in Mozilla and Chrome but not in IE. Can anyone help me tweak this so it will work in all browsers? here is the code:
function playerSearch(phrase, _name){
var words = phrase.value.toLowerCase().split(" ");
var table = document.getElementsByName(_name)[0];
var ele;
for (var r = 1; r < table.rows.length; r++){
ele = table.rows[r].innerHTML.replace(/<[^>]+>/g,"");
var displayStyle = 'none';
for (var i = 0; i < words.length; i++) {
if (ele.toLowerCase().indexOf(words[i])>=0)
displayStyle = '';
else {
displayStyle = 'none';
break;
}
}
table.rows[r].style.display = displayStyle;
}
}
Here is the function call from the html page:
<span id="filter" class="filter">Player Search:<input onkeyup="playerSearch(this, 'currentTable')" type="text"></span>
It sounds like IE is insisting on inserting a tBody dom element. try altering var table = document.getElementsByName(_name)[0]; to var table = document.getElementsByName(_name)[0].tBodies[0];
EDIT
After reading the comments below and doing a bit of googling it turns out that name is not a global attribute and not a valid attribute on tables according to the HTML4.01 specification (this is unchanged in the current draft of the HTML5 specification) this is unfortunatly one of the times when IEs javascript engine is being a stickler for the rules returning an array of 0 length when you do document.getElementsByName(_name) , and the other browser manufacturers are actually breaking the specification.
Here is my answer which works in all browsers. Separate function are given for IE, NetScape and all other browsers.
<script type="text/javascript" language="JavaScript">
var OtherBrowser = (document.getElementById);
var IE4 = (document.all);
var NS4 = (document.layers);
var win = window;
var n = 0;
function findInPage(str) {
var txt, i, found;
if (str == "") {
alert("Enter some thing to search");
return false;
}
else if (IE4) {
txt = win.document.body.createTextRange();
for (i = 0; i <= n && (found = txt.findText(str)) != false; i++) {
txt.moveStart("character", 1);
txt.moveEnd("textedit");
}
if (found) {
txt.moveStart("character", -1);
txt.findText(str);
txt.select();
txt.scrollIntoView();
n++;
}
else {
if (n > 0) {
n = 0;
findInPage(str);
}
else
alert("Sorry, we couldn't find.Try again");
}
}
else if (OtherBrowser) {
if (!win.find(str)) {
while (win.find(str, false, true, false, false, false, true))
n++;
}
else if (win.find(str)) {
n++;
}
}
if (NS4) {
if (!win.find(str)) {
while (win.find(str, false, true, false, false, false, true))
n++;
}
else if (win.find(str)) {
n++;
}
}
return false;
}
</script>
If you want to save yourself some trouble while applying some slick table functionality (including filters, which I think you're trying to achieve here), I suggest DataTables.
http://datatables.net/
Related
This function organize list, not organized in alphabetical order: ascending/descending in Firefox and Internet Explorer.
In google chrome and Edge is working.
Here is code:
<script type="text/javascript">
window.onload = function () {
var desc = false;
document.getElementById("Order").onclick = function () {
sortUnorderedList("PostList", desc);
desc = !desc;
return false;
}
}
function compareText(a1, a2) {
var t1 = a1.innerText,
t2 = a2.innerText;
return t1 > t2 ? 1 : (t1 < t2 ? -1 : 0);
}
function sortUnorderedList(ul, sortDescending) {
if (typeof ul == "string") {
ul = document.getElementById(ul);
}
var lis = ul.getElementsByTagName("li");
var vals = [];
for (var i = 0, l = lis.length; i < l; i++) {
vals.push(lis[i]);
}
vals.sort(compareText);
if (sortDescending) {
vals.reverse();
}
ul.innerHTML = '';
for (var i = 0, l = vals.length; i < l; i++) {
ul.appendChild(vals[i]);
}
}
</script>
Try this:
document.getElementById("hello").onclick = talk;
function talk()
{
alert('It works!');
}
<a id="hello">Click me!</a>
It seems to be right. Is your element ID right? Check if you have more then one element using the same ID also. If you can, post you HTML code.
It's bad if you use code of unknown source, you might even get in trouble with copyrights! Here is a simplified version (No, I won't apologize for the use of Bubble-sort!) that should be easy to follow.
Edited on request of OP to add an example of deep copying
<!DOCTYPE html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<script type="text/javascript">
// as shown in http://stackoverflow.com/questions/3066427/copy-all-childnodes-to-an-other-element-in-javascript-native-way
function copyElements (dst,src){
while (src.hasChildNodes()) {
dst.appendChild(src.removeChild(src.firstChild))
}
}
function sortUnorderedList(element_id, direction){
var table = document.getElementById(element_id);
var rows,i,tmp;
// get the rows in the order as they are
rows = table.rows;
i = rows.length - 1;
// tmp must be a node for this highly simplified stuff to work
tmp = document.createElement("getoffmylawn");
// do a simple Bubble sort (sorts lexically, maybe not what you want!)
// Assumes things to sort are in the first cell, adjust if necessary
for(; i > 0; i--){
for(j = 0;j < i; j++){
if(direction === false){
if(rows[j].firstChild.firstChild.textContent < rows[j+1].firstChild.firstChild.textContent){
copyElements (tmp , rows[j]);
copyElements (rows[j] , rows[j+1]);
copyElements (rows[j+1] , tmp);
}
}
else{
if(rows[j].firstChild.firstChild.textContent > rows[j+1].firstChild.firstChild.textContent){
copyElements ( tmp , rows[j]);
copyElements (rows[j] , rows[j+1]);
copyElements (rows[j+1] , tmp);
}
}
}
}
}
window.onload = function () {
var desc = false;
document.getElementById("Order").onclick = function () {
sortUnorderedList("FileList", desc);
desc = !desc;
return false;
};
};
</script>
</head>
<body>
<table id="FileList">
<tr><td>Foo</td></tr>
<tr><td>Bar</td></tr>
<tr><td>Foobar</td></tr>
<tr><td>Something</td></tr>
<tr><td>Anything</td></tr>
<tr><td>Somehwere</td></tr>
<tr><td>elsewhere</td></tr>
<tr><td>completely lost</td></tr>
</table>
<button id="Order">Sort</button>
</body>
</html>
If you think the order is wrong you have found out what "lexical order" actually means.
To work in Internet Explorer and Firefox, I replaced:
innerText to textContent
I have been trying to make a very simple search engine for a school project, but I can't figure out how to compare the user input I receive (a string from an input-textbox) to data I have in an array (A NodeList object with a bunch of names).
I will elaborate a little bit on exactly what I want to achieve: I have a HTML page which contains a input form and a bunch of movie titles (in format) and I want to compare what the user types in the input form to the movie titles that are on the page. If the user input matches one of the movie titles I want that movies' highlighted, and if the input doesn't match any movies, I just want an alert box to pop up saying that the input didn't match any movies.
I have been trying to do it like this:
var All_Titles = document.getElementsByTagName("h3");
var User_Input = document.forms["search_form"];
function InputValidation() {
var x = document.forms["search_form"]["input_box"].value;
if (x == "" || x == null) {
alert("You haven't entered anything");
} else {
Search();
}
}
function Search() {
if (User_Input == All_Titles) {
document.writeln("It worked!");
} else {
alert("No matched movies");
}
}
As you can see, I first capture the user input in a var called "User_Input", and I do the same with the movie titles.
Then, I check if the user has actually any text into the search form; if they didn't, they receive an error message and if they did, I run the Search() function, which is defined below.
Here comes the tricky part: I don't really know how I can compare the user input to the 's that I have! I have tried it with an If-Statement as you can see, but that doesn't seem to work.
Can anyone assist me with this problem?
I prefer using jQuery witch allows to do the same with less coding.
using jQuery:
<script type="text/javascript">
function search() {
var flag = false;
movieName = $("#movieSearch").val();
$(".moviesList").children("h3").each(function(index) {
if (movieName === $(this).html()) {
$(this).css("background-color", "yellow");
flag = true;
} else {
$(this).css("background-color", "white");
}
});
if(flag == false) {
alert("no such movie was found");
}
}
</script>
same code but only JS (less readable and maintainable):
<script type="text/javascript">
function search() {
var flag = false;
movieName = document.getElementById("movieSearch").value;
movies = document.getElementsByTagName("h3");
for (var i = 0; i < movies.length; i++) {
if (movieName === movies[i].innerHTML) {
movies[i].style.backgroundColor = "yellow";
flag = true;
} else {
movies[i].style.backgroundColor = "white";
}
}
if(flag == false) {
alert("no such movie was found");
}
}
</script>
HTML:
<div class="moviesList">
<h3>HHH</h3>
<h3>mmm</h3>
<h3>123</h3>
</div>
<input type="text" id="movieSearch" />
<button onclick="search()">Search By Name</button>
jQuery is a very common js library. use it by simply adding:
<script src="http://code.jquery.com/jquery-latest.min.js"></script>
You may try this example
var search = function() {
var movie = document.getElementById("movie").value;
if(!movie.replace(/\s+/g,'')) {//remove blank spaces
alert('Movie name required !');
return;
}
movie = movie.toUpperCase();
var list = document.getElementById('movieList');
list = list.getElementsByTagName('li');//movie title list
var i = list.length, html, flag = false, matches = [];
while(i --) {//walk through the list
html = list[i].innerHTML.toUpperCase();//get the movie in the li
if(-1 !== html.indexOf(movie)){//-1, string not found
flag = true;
matches.push(html);
}
}
if(!flag) {
alert('No match found !');
} else {
alert('Matched: ' + matches.join(', '));//show matching movies csv
}
};
<ul id="movieList">
<li>Star Wars</li>
<li>X-Men</li>
<li>MIB</li>
</ul>
<p>
Search <input type="text" id="movie"/><input type="button" onclick="search()" value="Search"/>
</p>
Use a loop to iterate through all the titles, comparing each with the user input
function Search() {
User_Input = User_Input.trim();
var i, len, title;
len = All_Titles.length;
for(i = 0; i < len; i++){
title = All_Titles[i].innerHTML.trim();
if(User_Input === title){
document.writeln("It worked!");
return;
}
}
if(i === len){
alert("No matched movies");
}
}
Here is my solution
function Search()
{
if (All_Titles.indexOf(User_Input)>-1)
{
alert("yes");
}
else
{
alert("No matched movies");
}
}
This solution may work for you.. try it
var arr = [];
for (var i = 0, ref = arr.length = All_Titles.length; i < ref; i++) {
arr[i] = All_Titles[i];
}
function Search()
{
if (arr.indexOf(User_Input)>-1)
{
alert("yes");
}
else
{
alert("not");
}
}
Array.prototype.indexOf = function(elt)
{
var len = this.length >>> 0;
var from = Number(arguments[1]) || 0;
from = (from < 0)
? Math.ceil(from)
: Math.floor(from);
if (from < 0)
from += len;
for (; from < len; from++)
{
if (from in this &&
this[from] === elt)
return from;
}
return -1;
};
Hi I am trying to compare two arrays to each other and then hide a list element if any of the values match.
One array is tags that are attached to a list item and the other is user input.
I am having trouble as I seem to be able to cross reference one user input work and can't get multiple words against multiple tags.
The amount of user input words might change and the amount of tags might change. I have tried inArray but have had no luck. Any help would be much appreciated. See code below:
function query_searchvar() {
var searchquery=document.navsform.query.value.toLowerCase();
if (searchquery == '') {
alert("No Text Entered");
}
var snospace = searchquery.replace(/\s+/g, ',');
event.preventDefault();
var snospacearray = snospace.split(',');
$('li').each(function() {
var searchtags = $(this).attr('data-searchtags');
//alert(searchtags);
var searcharray = searchtags.split(',');
//alert(searcharray);
var searchtrue=-1;
for(var i = 0, len = searcharray.length; i < len; i++){
if(searcharray[i] == searchquery){
searchtrue = 0;
break;
}
}
if (searchtrue == 0) {
$(this).show("normal");
}
else {
$(this).hide("normal");
}
});
}
Okay so I've tried to implement the code below but have had no luck. It doesn't seem to check through both arrays.
function query_searchvar()
{
var searchquery=document.navsform.query.value.toLowerCase();
if(searchquery == '')
{alert("No Text Entered");
}
var snospace = searchquery.replace(/\s+/g, ' ');
event.preventDefault();
var snospacearray = snospace.split(' ');
alert(snospacearray[1]);
$('li').each(function() {
var searchtags = $(this).attr('data-searchtags');
alert(searchtags);
var searcharray = searchtags.split(' ');
alert(searcharray[0]);
jQuery.each(snospacearray, function(key1,val1){
jQuery.each(searcharray,function(key2,val2){
if(val1 !== val2) {$(this).hide('slow');}
});
});
});
}
Working code:
function query_searchvar()
{
var searchquery=document.navsform.query.value.toLowerCase();
if(searchquery == '')
{alert("No Text Entered");
}
var queryarray = searchquery.split(/,|\s+/);
event.preventDefault();
$('li').each(function() {
var searchtags = $(this).attr('data-searchtags');
//alert(searchtags);
var searcharray = searchtags.split(',');
//alert(searcharray);
var found = false;
for (var i=0; i<searcharray.length; i++)
if ($.inArray(searcharray[i], queryarray)>-1) {
found = true;
break;
}
if (found == true )
{
$(this).show("normal");
}
else {
$(this).hide("normal");
}
});
}
var snospace = searchquery.replace(/\s+/g, ',');
var snospacearray = snospace.split(',');
Note that you can split on regular expressions, so to the above would equal:
var queryarray = searchquery.split(/,|\s+/);
To find whether there is an item contained in both arrays, use the following code:
var found = searcharray.some(function(tag) {
return queryarray.indexOf(tag) > -1;
});
Although this will only work for ES5-compliant browsers :-) To support the others, use
var found = false;
for (var i=0; i<searcharray.length; i++)
if ($.inArray(searcharray[i], queryarray)>-1) {
found = true;
break;
}
In plain js, without jQuery.inArray:
var found = false;
outerloop: for (var i=0; i<searcharray.length; i++)
for (var j=0; j<queryarray.length; j++)
if (searcharray[i] == queryarray[j]) {
found = true;
break outerloop;
}
A little faster algorithm (only needed for really large arrays) would be to sort both arrays before running through them linear.
Here's psuedo code that should solve your problem.
get both arrays
for each item in array 1
for each element in array 2
check if its equal to current element in array 1
if its equal to then hide what you want
An example of this coude wise would be
jQuery.each(array1, function(key1,val1){
jQuery.each(array2,function(key2,val2){
if(val1 == val2) {$(your element to hide).hide();}
});
});
If there's anything you don't understand please ask :)
I have a long table with many many columns and it looks really ugly for the users. What I wanted to do was create a simple button that would act as a switch, to turn on and off some of the columns.
Some of the columns are not needed, so what I did was add a class to every that wasn't needed, eg: ....
Now, what I thought I could do was this:
var hidden = 1;
function toggleTable(){
element_array = document.getElementsByClassName('disabled');
for(i = 0; i < element_array.length; i++){
if(hidden == 1){
element_array[i].style.display = 'none';
}else{
element_array[i].style.display = '';
}
}
if(hidden == 1) hidden = 0;
else hidden = 1;
}
This works for the most part in Firefox, but some quick tests in IE(7+8) and I get the following:
Message: Object doesn't support this property or method
Obviously indicating that IE doesn't want to let me simply change "display: none;" for something like table columns/rows.
I can't think of any workarounds. Ideally I'd like a fully cross-compatible solution to toggling the display of certain table columns,but if it's not compatible in the older browsers (eg: IE6) then that would also be OK.
The error that you're getting is not because IE doesn't want to set the display property, it's because the getElementsByClassName method isn't implemented in IE. If you want an implementation of that methods you can use this one which was written by Dustin Diaz.
function getElementsByClass(searchClass,node,tag) {
var classElements = new Array();
if ( node == null )
node = document;
if ( tag == null )
tag = '*';
var els = node.getElementsByTagName(tag);
var elsLen = els.length;
var pattern = new RegExp("(^|\\s)"+searchClass+"(\\s|$)");
for (i = 0, j = 0; i < elsLen; i++) {
if ( pattern.test(els[i].className) ) {
classElements[j] = els[i];
j++;
}
}
return classElements;
}
Then you would re-write your method as follows.
var hidden = 1;
function toggleTable(){
var element_array = getElementsByClass('foo');
for(i = 0; i < element_array.length; i++){
if(hidden == 1){
element_array[i].style.display = 'none';
}else{
element_array[i].style.display = '';
}
}
if(hidden == 1) hidden = 0;
else hidden = 1;
}
toggleTable();
And what about jQuery.toggle()?
$(".disabled").toggle();
I am having issues figuring out how to resolve the getElementsByClassName issue in IE. How would I best implement the robert nyman (can't post the link to it since my rep is only 1) resolution into my code? Or would a jquery resolution be better? my code is
function showDesc(name) {
var e = document.getElementById(name);
//Get a list of elements that have a class name of service selected
var list = document.getElementsByClassName("description show");
//Loop through those items
for (var i = 0; i < list.length; ++i) {
//Reset all class names to description
list[i].className = "description";
}
if (e.className == "description"){
//Set the css class for the clicked element
e.className += " show";
}
else{
if (e.className == "description show"){
return;
}
}}
and I am using it on this page dev.msmnet.com/services/practice-management to show/hide the description for each service (works in Chrome and FF). Any tips would be greatly appreciated.
I was curious to see what a jQuery version of your function would look like, so I came up with this:
function showDesc(name) {
var e = $("#" + name);
$(".description.show").removeClass("show");
if(e.attr("class") == "description") {
e.addClass("show");
} else if(e.hasClass("description") && e.hasClass("show")) {
return;
}
}
This should support multiple classes.
function getElementsByClassName(findClass, parent) {
parent = parent || document;
var elements = parent.getElementsByTagName('*');
var matching = [];
for(var i = 0, elementsLength = elements.length; i < elementsLength; i++){
if ((' ' + elements[i].className + ' ').indexOf(findClass) > -1) {
matching.push(elements[i]);
}
}
return matching;
}
You can pass in a parent too, to make its searching the DOM a bit faster.
If you want getElementsByClassName('a c') to match HTML <div class="a b c" /> then try changing it like so...
var elementClasses = elements[i].className.split(/\s+/),
matchClasses = findClass.split(/\s+/), // Do this out of the loop :)
found = 0;
for (var j = 0, elementClassesLength = elementClasses.length; j < elementClassesLength; j++) {
if (matchClasses.indexOf(elementClasses[j]) > -1) {
found++;
}
}
if (found == matchClasses.length) {
// Push onto matching array
}
If you want this function to only be available if it doesn't already exist, wrap its definition with
if (typeof document.getElementsByClassName != 'function') { }
Even easier jQuery solution:
$('.service').click( function() {
var id = "#" + $(this).attr('id') + 'rt';
$('.description').not(id).hide();
$( id ).show();
}
Why bother with a show class if you are using jQuery?
Heres one I put together, reliable and possibly the fastest. Should work in any situation.
function $class(className) {
var children = document.getElementsByTagName('*') || document.all;
var i = children.length, e = [];
while (i--) {
var classNames = children[i].className.split(' ');
var j = classNames.length;
while (j--) {
if (classNames[j] == className) {
e.push(children[i]);
break;
}
}
}
return e;
}
I used to implement HTMLElement.getElementByClassName(), but at least Firefox and Chrome, only find the half of the elements when those elements are a lot, instead I use something like (actually it is a larger function):
getElmByClass(clm, parent){
// clm: Array of classes
if(typeof clm == "string"){ clm = [clm] }
var i, m = [], bcl, re, rm;
if (document.evaluate) { // Non MSIE browsers
v = "";
for(i=0; i < clm.length; i++){
v += "[contains(concat(' ', #"+clc+", ' '), ' " + base[i] + " ')]";
}
c = document.evaluate("./"+"/"+"*" + v, parent, null, 5, null);
while ((node = c.iterateNext())) {
m.push(node);
}
}else{ // MSIE which doesn't understand XPATH
v = elm.getElementsByTagName('*');
bcl = "";
for(i=0; i < clm.length; i++){
bcl += (i)? "|":"";
bcl += "\\b"+clm[i]+"\\b";
}
re = new RegExp(bcl, "gi");
for(i = 0; i < v.length; i++){
if(v.className){
rm = v[i].className.match(bcl);
if(rm && rm.length){ // sometimes .match returns an empty array so you cannot use just 'if(rm)'
m.push(v[i])
}
}
}
}
return m;
}
I think there would be a faster way to iterate without XPATH, because RegExp are slow (perhaps a function with .indexOf, it shuld be tested), but it is working well
You can replace getElementsByClassName() with the following:
function getbyclass(n){
var elements = document.getElementsByTagName("*");
var result = [];
for(z=0;z<elements.length;z++){
if(elements[z].getAttribute("class") == n){
result.push(elements[z]);
}
}
return result;
}
Then you can use it like this:
getbyclass("description") // Instead of document.getElementsByClassName("description")