I've an html table that contains a list of names within sentences, which are inside <td> elements (i.e. <td>Mike (Jeremy's father) used to play piano</td> or <td>The only one who speaks french is Desmond, but Mike speaks very well spanish</td>). I would like to count how many times a specific name appears within the entire table. I need this to be done through javascript/jquery, and not via php.
Any help would be appriciated!
You could use
var nbMikeInTheTable = (' '+$('table').text()+' ').split('Mike').length-1;
(the added ' ' are for the compatibility with the buggy implementation of split on IE)
Or, if you want to be sure it's the entire word and it's not cut by HTML
var nbMikeInTheTable = $('table').html().split(/\W+/).filter(
function(v){return v=="Mike"}
).length;
If you want to guard against end of words and end of cells and be sure it's not part of an attribute of a td, it's a little more complex :
var nbTimesMikeIsInTheTable = $('table td').map(function(){
return $(this).text().split(/\W+/).filter(function(v){return v=="Mike"}).length
}).toArray().reduce(function(p,v){return p+v});
This isn't elegant but is probably not needed ;)
Demonstration (open the console)
Related
I have a table with some items and those items can be selected by adding a tick. Check the image attached:
What I need to achieve is to hide the row which does not contain any ticks to be not visible. This is because in my app I have to generate lists of the items contains only ticks in another view. So when I will press the generate button that row will be hidden.
I just want to say if that row does not contain any 'glyphicon-ok' need to be deleted/hidden when I will generate the view with the list of those items.
I tried something like this:
SveCrf.prototype.hideRowWhereNoTicksForm = function () {
var tr = document.getElementsByTagName('tr');
for (var i = 0; i < tr.length; i++) {
switch (tr.item(i).getElementsByTagName('td').item(0).className) {
case "glyphicon-ok":
tr.item(i).style.display = "none";
break;
}
}
}
This doesn't do anything.
I would like to see an example of being able to resolve this issue.
Correct me if I'm wrong but you don't seem to have provided HTML you want to act upon but just a screenshot and a link to some RoR code in the comments that generates the HTML. Also you don't show how you try to execute SveCrf.prototype.hideRowWhereNoTicksForm, and furthermore I'm not really sure at all what you are trying to do with switch/case (I also don't understand what item is supposed to be; this is where providing us with actual HTML might have helped).
In addition, as I've alluded to in some comments of mine, you are really trying to do two things. I don't know if you've seen this Stackoverflow page yet about creating "a Minimal, Complete, and Verifiable example" but I think reviewing that will help improve your StackOverflow experience moving forward (and also for me it validated my suggestion of "divide and conquer").
All of which I think made it hard for you to get the help you desired. In any case below I'm providing some sample HTML with a table containing four rows total, two with a cell that contains the class foo, and two that don't. Beneath that is my non-jQuery code selecting the rows with no cells containing the class foo, and then hiding them; furthermore there is a demo of the same functionality using jQuery at https://repl.it/#dexygen/HideRowsWithNoCellsWithClass
<table border="1">
<tr><td class='foo'>foo</td><td></td><td></td></tr>
<tr><td></td><td>bar</td><td></td></tr>
<tr><td></td><td></td><td>baz</td></tr>
<tr><td class="foo">foo</td><td>bar</td><td>baz</td></tr>
</table>
/*
We cannot call `filter` directly on an HTMLCollection such as returned by
"document.getElementsByTagName('tr')" as it is not a bona fide array, so we use
"[].filter.call()", and we return only those rows that *fail* the test
"row.querySelector('td.foo')", then we loop over these with `forEach` and hide them
*/
[].filter.call(document.getElementsByTagName('tr'), function(row) {
return !row.querySelector('td.foo');
}).forEach(function(row) {row.style.display = 'none'});
I'm developing an asynchronous database searching tool. It currently works with firefox and chrome, but has one enormous hiccup with internet explorer (version 8).
Students can enter their prospective MCAT and GPA scores and then jquery sorts out the schools where they place in the top 25% or the middle 50%. Basically, it's a neurotic premed student's dream (or nightmare).
The jquery cycles through the JSON data, displaying each item that matches the criteria in a <li> item. Again, it works great in ff and chrome, but in internet explorer it refuses to display the list items. It does, however, display the proper count of items, which means that the json data is going through properly.
After searching through stackoverflow I saw some commentary (colorful, often!) on how IE refuses to allow placing elements in tables and some other innerhtml elements using jquery.
I'm wondering if this is the problem, and though I found a similar problem on this question I can't quite figure out how to adapt it for my project (I'm new to javascript).
Any help would be wonderful. The code can be found below.
-samuel
$.getJSON("schoolgrabber.php?jsoncallback=?", function(data){
//loop through the items in the array
for(var x=0; x < data.length; x++){
if( MCAT >= data[x].TopPercentileMCAT && data[x].TopPercentileMCAT!=''){
var li = $("<li>").appendTo("#schoollist");
var school= data[x].School;
//add the actual information into the li using a chain event
//the event begins by appending an <a> tag, with the name of the school inside (text(data[x].School)
//then it adds a unique id, which is the iteration number the counter is on above (x)
//then it adds the url, and adds the school variable, which allows the schools to be clicked over to the results page
//then it puts all of that inside li, as declared above (slightly backwards, but hey)
$("<a>").text(data[x].School).attr("id",x).attr("href", "results.php?school=" + school).appendTo(li);
$("#schoollist li").addClass("school");
var quantity = $(".school").length;
$('#schoolquantity').empty();
$('#schoolquantity').append(quantity);
}}
});
Instead of using jQuery and chaining to build up your DOM, try instead to build out a string of the HTML you want rendered and add that completed string just the one time.
Not only might it fix your bug, but you'll also get better performance. What I'm doing is building up the list's full HTML and calculating the quantity. Then, when I'm completely finished building the HTML, I add it to the DOM. IMO the way I have below is also more readable. Note that I haven't tested this, but you should get the idea:
$.getJSON("schoolgrabber.php?jsoncallback=?", function(data){
//loop through the items in the array
var html = [];
var parentElement = $("#schoollist");
var quantity = 0;
for(var x=0; x < data.length; x++){
if( MCAT >= data[x].TopPercentileMCAT && data[x].TopPercentileMCAT!=''){
var school= data[x].School;
html.push("<li class=\"school\">");
html.push(" <a id=\"" + x + "\" href=\"results.php?school=" + school "\">");
html.push( data[x].School);
html.push(" </a>");
html.push("</li>");
quantity++;
}
}
parentElement.html(html.join(""));
$('#schoolquantity').html(quantity);
});
Remember that everytime you alter the DOM the browser has to do a bunch of stuff to update the webpage. This is costly. You want to avoid adding/grabbing from the DOM as much as possible and instead "batch" your alterations (As a side note, if you want to alter the DOM alot without "batching", take a look at the jquery detach method).
Good luck, hope this works for you!
I am relatively new at javascript, and found an interesting behavior that I can't explain today. I have a custom <hr> (with an image) on a website, which displays oddly in IE7 and below. To overcome this, I wanted to use replaceChild() in combination with getElementsByTag(). Initially, I simply tried to loop over the list, so:
var hrules = document.getElementsByTagName('hr');
for (var i=0; i < hrules.length; i++) {
var newHrule = document.createElement("div");
newHrule.className = 'myHr';
hrules[i].parentNode.replaceChild(newHrule, hrules[i]);
document.write(i);
}
However, this does not work: it actually only gets half the elements, skipping every other one. Printing i gives half-integer values of the actual number of <hr> elements in the document (e.g. if there are 7 <hr/> elements, it prints 4. By contrast, the following does work:
var hrules = document.getElementsByTagName('hr');
var i = 0;
while (i < hrules.length) {
var newHrule = document.createElement("div");
newHrule.className = 'myHr';
hrules[i].parentNode.replaceChild(newHrule, hrules[i]);
document.write(i);
}
i is printed the same number of times as there are hrules in the document (but of course is always 0, since I'm not incrementing it), and the hrules are replaced correctly. I recognize that the while here might as well be while(true)--it's just going until it runs out of <hr> elements, but appears to stop after that (it's not printing any more 0s).
I've tried this with a number of different types of elements, and observed that this only occurs when replacing one kind of element with another. I.e., replacing p with div, span with p, etc. If I replace p with p, div with div, etc. the original example works correctly.
Nothing in the documentation I've found (w3schools, various Google search, here, etc.) suggests an obvious answer.
What is going on here? First, why does the second example I offered work - is replaceChild() iterating over the elements automatically? Second, why is the behavior different for different types of element?
document.getElementsByTagName is a live access to all the HR elements in the document - it's updated whenever you change the document. You don't get a snapshot of all the HRs in the document whenever you call it.
So, with the first code, you are both incrementing i and reducing the size of hrules.length each time round the loop. This explains why you only see half the steps you expect.
Here's the solution I ended up using, in case anyone else (like #Pav above) is curious.
var hrules = document.getElementsByTagName('hr');
/* Each repetition will delete an element from the list */
while (hrules.length) {
var newHrule = document.createElement("div");
newHrule.className = 'ieHr';
/* Each iteration, change the first element in the list to a div
* (which will remove it from the list and thereby advance the "head"
* position forward. */
hrules[0].parentNode.replaceChild(newHrule, hrules[0]);
}
Essentially, what happens is you get a list of all the hrules in the document. This list is dynamically updated as you interact with it (see Matthew Wilson's answer). Each time you change the first element of the list to a div, it gets removed from the list, and the list is updated accordingly. The result is that you simply need to act on the first element of the list each time until the length of the list is 0.
That's admittedly a little counterintuitive, but it's how the list works.
I'm struggling to decipher a way to remove several specific href elements which contain no IDs and are children of individual parents with no IDs.
The best I can manage is identifying the four offending, out of 8 or 9 href tags (and the number may vary), by a specific word within the URL itself. For this, I do the following:
<script language=javascript>
var xx = document.getElementById('theID').getElementsByTagName('a');
var ptn=/\=media/;
for(var i=0; i<xx.length; i++) {
if(ptn.exec(xx[i])){
alert(xx[i]);
}
}
</script>
Of course all this gives me is the four specific URLs within the href where "=media" is present. Now, somehow, I need to be able to remove either these href elements, or their parent elements (which happen to be unordered list tags). It's not until I get a level higher (table cell) that I gain access to an element ID, or anything distinguishing besides a particular word within the URL itself.
I'm open to any approach at this point - PHP may be an option (I really haven't explored this yet), but for this, javascript was my first logical choice. I can't tamper with the page that generates the links directly, only a secondary page which gets included at page load time.
Any pointers on how to solve this??
======================== final solution =====================
<script language=javascript>
var xx = document.getElementById('theID').getElementsByTagName('a');
var ptn=/\=media/;
for(var i=0; i<xx.length; i++) {
while(ptn.exec(xx[i].href)){
alert(xx[i]);
xx[i].parentNode.removeChild(xx[i]);
}
}
</script>
You don't need the ID to remove an element. You only need a reference to the element (which you seem to have).
instead of this:
alert(xx[i]);
try this:
XX[i].parentElement.removeChild(xx[i]);
You can call removeChild() on the parent element, like so:
xx[i].parentNode.removeChild(xx[i]);
As a side note, your regular expression isn't being executed on the href property. Change your if statement to:
if(ptn.exec(xx[i].href)){
var parent = xx[i].parentNode;
parent.removeChild(xx[i]);
http://www.onlinetools.org/articles/unobtrusivejavascript/chapter2.html has some nice examples of similar operations (scroll down).
This is a challenge question / problem. Hope you find it interesing.
Scenario: You have a very long list (unreasonably long) in a single column. It would be much better displayed in multiple shorter columns. Using jQuery or another tool, what do you do?
The format of the list is as follows:
<div class="toc">
<dl>
<dt>item 1</dt>
<dd>related to 1</dd>
<dt>item 2</dt>
<dd>related to 2</dd>
<dt>item 3</dt>
<dd>related to 3</dd>
<dt>item 4</dt>
<dd>related to 4</dd>
<dt>item 5</dt>
<dd>related to 5</dd>
<dt>item 6</dt>
<dd>related to 6</dd>
<dt>item 7</dt>
<dd>related to 7</dd>
<dt>item 8</dt>
<dd>related to 8</dd>
<dt>item 9</dt>
<dd>related to 9</dd>
<dt>item 10</dt>
<dd>related to 10</dd>
</dl>
</div>
Caveat: The dd's may contain nested dl's, dt's, & dd's.
Also be sure to keep related items in the same column (ie. if dt 7 is col x, so should dd 7).
This problem inspired by the somewhat ridiculously laid out Zend Framework manual.
Edit: See below for answer.
i would do a count of the array of $("dt") then if it's over a certain size inject a closing and opening then using styling to float them into columns.
Josh
Ignoring:
Also be sure to keep related items in the same column (ie. if dt 7 is col x, so should dd 7).
one possible solution could be column-count, e.g:
http://pici.se/pictures/small/vekPcSkFE.png
However, it's a part of CSS 3 so browser support ... you know. :(
I would first write the complete data in a json then check the amount of dt's + dd's + related children amount by considering this sum as total row amount.
Then i would check the predefined row amount per column and divide this json into proper parts.
And finally from this parts i'd create columns without breaking related items.
Sinan.
Answer So this problem is more difficult than it first appears.
My initial thought was that I would wrap the column in <table><tr><td></td></tr></table> and then after every nth parent dd output a </td><td>. Simple, right? Except you can't really output a closing tag like that. Ultimately when you write $('</td><td>').after('.toc > dl > dd'), you'll be creating nodes - that's nodes with opening and closing tags. Since you must create nodes, the browser will ignore the first closing tag.
Well, let's say that you solve that problem somehow. What are you're iteration conditions? My first attempt was to construct a for loop. It seems reasonable. For every nth parent dd, do whatever you need to do. However, how do you construct those conditions in jQuery? You have less than, greater than, and equal. But you don't have greater than or equal to (>=) or less than or equal to (<=), and this is a critical distinction.
You might try something like this:
for (i = 0; i < len; i+=40) { // where 40 determines the # per col
get elements i thru i+40;
wrap this set and float left
}
So how would you do this in jQuery?
// note that you must use > to prevent nested descendants from being selected
var len = jQuery('.toc > dl > dd').size()
for (i = 0; i < len; i+=40) {
// this selector says give me the first 40 child elements
// whatever type of element they may be
$('.toc > dl > *:gt('+i+'):lt('+(i+40)').wrapAll('<div style="float:left">');
// however because we don't have >= and :gt won't accept negatives as an input
// we must either do a special case for the first iteration
// or construct a different selector
$('.toc > dl > *:eq('+i+')', ' +
'.toc > dl > *:gt('+i+'):lt('+(i+40)')
.wrapAll('<div style="float:left">');
}
You could also do something with jQuery's add() method to add the first element of each iteration to your set, but you must maintain document order in your selection or jQuery will rearrange the set, so you have to do that first.
Ultimately, the for loop made sense initially, but it ran into problems with challenging selectors. Of course, we're not using the $('selector').each(function () { }); construct because that would only be useful if we could output independent closing tags.
So what did I end up with? Final Answer:
$('.toc').after('<div id="new"></div>');
do {
var curSet = $('.toc > dl > *:lt(40)')
.appendTo('#new').wrapAll('<div style="float:left"></div>');
} while (curSet.size());
This approach appends a new div after the old one. Then iteratively grabs the first 40 elements from the old and appends them to the new div after wrapping them in a div that will float left, looping as long as there are elements left to grab, and it maintains order.
Not terribly complicated after you figure it out, but there were a few gotcha's throughout the problem that I hope you find interesting. I did.
To finish up the ultimate goal of making the documentation significantly more useful:
I added some style and used the dt's as togglers to show the dd's. I also used a simple php proxy wrapper (5-10 LOC) so I could bring in any given, desired doc page thru an ajax call without remote ajax warnings.
I ended up with a nice little document in a single, navigable page that loads in < 2 secs (and uses ajax to load all subsequent pages in < 1 sec) rather than a monstrous page that takes 15-20 sec to load per page!
Problem solved. Something much more enjoyable and useful in 10-15 lines of javascript (total with the reorganizing, toggling, and ajax code), < 10 lines of PHP, and a few style rules.
No more slow Zend docs and endless scrolling.