I am looking a better method than "find" to find all the elements within an element:
<table>
<tr>
<td class="test">
Test
</td>
</tr>
<tr>
<td class="test">
Test
</td>
</tr>
<tr>
<td class="test">
Test
</td>
</tr>
</table>
This is structure and and I have jquery of table as $table
I am willing to do something like this:
$table.find('.test');
Find is a very expensive call. I need a better way to do it. I know I can do it with direct selectors like this:
$('table .test');
But I am looking for an alternative for find which performs better. Find performs really bad especially in IE.
The biggest thing you can do is to improve the selector you are using:
$table.find('td.test');
Providing an element name speeds up the selection significantly. This is especially true in versions of IE before IE9, which lack the getElementsByClassName method.
If you have getElementsByClassName, this won't offer a significant improvement. If you don't, however, the improvement will be massive. jQuery has to select every element that might match, then test to see if it has the relevant class present. If you provide td, it will only test td elements. If you don't, it has to test every element, which will obviously take much longer.
find is slow in IE because IE does not implement a native getElementsByClassName. The fastest solution is apply knowledge of your DOM structure to the function that selects the elements. It is less flexible but always will be the fastest solution. In this case, it looks like the only elements that have the test class are td:
var rows = $table[0].rows, cells, i, j, numRows, numCells, result = [];
for(i = 0, numRows = rows.length; i<numRows; i++) { //Iterate over the table's rows
cells = rows[i].children;
for(j = 0, numCells = cells.length; j<numCells; j++) { //Iterate over the cells in the row
if(cells[j].className == "test") { //Won't be valid if the cells have more than one class
result.push(cells[j]);
}
}
}
This is just an example. Like I said, you sacrifice flexibility for speed, so you may need to tweak it to adapt to your particular structure but the principle remains.
I think if you use
document.getElementById('myTable')
on
<table id="myTable">
<tr>
<td class="test">
Test
</td>
</tr>
<tr>
<td class="test">
Test
</td>
</tr>
<tr>
<td class="test">
Test
</td>
</tr>
</table>
Will be fastest as possible.
I made a jsperf to test $('.test', $('#my_table')) vs $('table .test');
http://jsperf.com/find-vs-direct
It looks like $('.test', $('#my_table')) is the winner
Related
I'm trying to iterate through table rows and get each row which includes a specific value,
but it doesn't work for me.
I'm using .each() to iterate the rows and .within() on each $el,
inside that, I use cy.get('td').eq(1).contains('hello') but I the get assertion error:
Timed out retrying: Expected to find content: 'hello' within the element: <td> but never did.
when I console.log cy.get('td').eq(1) it yields the desired cell in each row and the test passes, so I don't understand why chaining .contains() doesn't work...
it('get element in table', () => {
cy.visit('http://localhost:3000/');
cy.get('tbody tr').each(($el) => {
cy.wrap($el).within(() => {
cy.get('td').eq(1).contains('hello') // contains() doesn't work
})
})
});
<table>
<thead>
<tr>
<th>Month</th>
<th>Savings</th>
</tr>
</thead>
<tbody>
<tr>
<td>January</td>
<td>$100</td>
</tr>
<tr>
<td>February</td>
<td>hello</td>
<td>$80</td>
</tr>
<tr>
<td>$10</td>
<td>hello</td>
</tr>
</tbody>
</table>
should('have.text', text) should work
cy.get('td').eq(1).should('have.text', 'hello')
If there's whitespace around text, use contain.text
cy.get('td').eq(1).should('contain.text', 'hello')
The simple answer is: don't :)
To be more specific use html attribute selection instead. The convention is to have an attribute named data-cy. Furthermore, I discovered it convenient to have a data-cy-identifier for when selecting specific rows. Since I'm not sure what you're trying with your code, I'll use a similar example that can hopefully get you going:
<table data-cy="expences">
<tr>
<td data-cy="month">January</td>
<td data-cy="price">$100</td>
</tr>
<tr data-cy="discounted">
<td data-cy="month">Feburary</td>
<td data-cy="price">$80</td>
</tr>
<tr data-cy="discounted">
<td data-cy="month">March</td>
<td data-cy="price">$10</td>
</tr>
</table>
You can of course do all sorts of combinations of this, but now you can do more specific and useful selections, such as:
cy.get('[data-cy="expenses"]').find('[data-cy="discounted"]').find('[data-cy="price"]').should(...)
And similar. This is flexible, because it reflects the structure of your data, and not the presentation, so you can change this to a list or whatever later. It avoids selecting of volatile things, so it's also more robust. It also uses a hierarchy rather than overly specific selectors.
The idea of adding things like data-cy-identifier allows you to do selections by ID (you can propagate it using javascript, angular, vue or whatever you use) and then checking things like the contents of a row with logically related items.
Hope it can get you going. Also I can recommend reading: https://docs.cypress.io/guides/references/best-practices.html
My html code is-
<tr class="selected">
<td id="participantID">XXXXX1234</td>
<input type="hidden" value="000001234" id="taxID">
<td id="fullName">Y, X</td>
</tr>
Here, I want to get hidden field value. I can not use ID of hidden field to get its value because there are multiple rows which can contain hidden field with same ID as "taxID". I want to get this value using <tr> class name.
i.e. selected.
I am using below code to get its value but it is giving me 'undefined' value.
var x = document.getElementsByClassName("selected")[0];
var y = x.getElementsByTagName("input")[0];
alert(y.value);
Alert statement shows undefined value. Am I missing something over here?
First, you cannot have multiple elements in a document with identical id values. That will have to be altered and that alone may solve your problem.
Second, your HTML is invalid. The input must be inside of a td.
Next, there is no reason to use getElementsByClassName() or getElementsByTagName() when you are looking for just one element - it's wasteful because you wind up searching the entire document when you are only interested in one item.
Also, both of those methods return "live" node lists which require re-scanning the entire document every time their results are referenced. The use cases for that are limited.
Instead use .querySelector() when you want to find just one item based on any valid CSS selector and .querySelectorAll() when you want to find a set of matching elements.
Assuming these things are corrected, you can do this:
var x = document.querySelector(".selected td input[type=hidden]");
alert(x.value);
<table>
<tr class="selected">
<td id="participantID">XXXXX1234
<input type="hidden" value="000001234" id="taxID">
</td>
<td id="fullName">Y, X</td>
</tr>
</table>
You need to have a table be the parent of a tr, then the DOM lookup will properly work. Also as noted by #Rory McCrossan you will want to wrap td tag around your input element:
var x = document.getElementsByClassName("selected")[0];
var y = x.getElementsByTagName("input")[0];
alert(y.value);
<table>
<tr class="selected">
<td id="participantID">XXXXX1234</td>
<td><input type="hidden" value="000001234" id="taxID" /></td>
<td id="fullName">Y, X</td>
</tr>
</table>
(Posted solution on behalf of the OP).
After removing ID of hidden field, it is working fine. Edited code is:
<tr class="selected">
<td id="participantID">XXXXX1234</td>
<input type="hidden" value="000001234" id="taxID">
<td id="fullName">Y, X</td>
</tr>
Looking for some help on how to capture value for a specific TD cell. The challenge i'm faced with is that I cannot modify the TD cell to include classes or modify them as the table content are created dynamically by another system.
I created the fiddle below to give you an idea of what the structure of the table. As i explained above since I cannot directly modify the table structure or add extra classes to make it easier to target the TD cell I want, I am trying to Macgyver my way out of this one.
What I am trying to accomplish is this:
1) Need to capture the Reference number (Always 11 characters long and only contains numbers).
2) Put the captured reference number into a variable.
3) Concatenate the variable to the following url http://test.com/UpdateNotification.asp?OEN= (variable here) &CL=ALL&SU=ALL
4) Replace the Reference number with the URL.
So I am really having problems with the step 1 of this process. I would appreciate some help on this.
http://jsfiddle.net/W4Km8/5665/
<DIV class="sm_content">
<table border="1" width="100%">
<tr>
<td class="ms-vb2">reference numbers</td>
<td class="ms-vb2">name</td>
<td class="ms-vb2">Description</td>
<td class="ms-vb2">Incident</td>
</tr>
<tr>
<td class="ms-vb2">20150715110</td>
<td class="ms-vb2">Jhonson Doe</td>
<td class="ms-vb2">Box of cereal and other stuff</td>
<td class="ms-vb2">123</td>
</tr>
<tr>
<td class="ms-vb2">20150715111</td>
<td class="ms-vb2">Bobby Boucher</td>
<td class="ms-vb2">Box of nails and stuff</td>
<td class="ms-vb2">124</td>
</tr>
</table>
</DIV>
In the future, please make an effort before asking. That said, coders gotta code.
$('.sm_content table tr:gt(0)').each(function() {
var myId = $(this).find('td').eq(0).text();
var myUrl = 'http://test.com/UpdateNotification.asp?OEN=' + myId;
$(this).find('td').eq(0).html('' + myId + '');
});
Fiddle demo
Or, if you didn't actually want the ID linked, just do this for the last line:
$(this).find('td').eq(0).text(myUrl);
You have different ways to do it.
(function($){
$('.sm_content tr').slice(1).each(function(){
var td = $(this).find('td').first();
var tdContent = td.html();
td.html('' + tdContent + '');
});
})(jQuery);
Load the entire page into a string. I've used phpfilegetcontents but for an html only page I'd manually copy and paste the page into a text box and use document.getElementbyid('box').innerHTML to load the page's contents into a string
Find the first cell: entirepage.indexOf('td')
entirepage.slice(start from location of td plus enough characters to get past the rest of the tag, end 11 characters later)
that's your reference number
find the nth instance of 'td'
and so on.
no jquery necessary
I'm trying to write a regular express that will capture an HTML table (and all it table data) that has a particular class.
For example, the table has a recapLinks class, its comprised of numerous table rows and table data and then terminated with . See below:
<table width="100%" class="recapLinks" cellspacing="0">
[numerous table rows and data in the table.]
</td></tr></tbody></table>
I'm using javascript.
The regex to capture this is pretty simple, if you can guarantee that there are never nested tables. Nested tabled become much trickier to deal with.
/<table[^>]*class=("|')?.*?\bCLASSNAMEHERE\b.*?\1[^>]*>([\s\S]*?)</table>/im
For instance, if an attribute before class had a closing > in it, which isn't likely, but possible, the regex would fall flat on it's face. Complex reges can try to prepare for that, but it's really not worth the effort.
However, jQuery all by itself can make this a breeze, if these elements are within the DOM. Regex can be easily fooled or tripped, deliberately or accidentally but that's why we have parsers. JQuery doesn't care what's nested or not within the element. It doesn't care about quote style, multiline, any of that.
$(document).ready(function () {
console.log($("table.myClassHere").prop("outerHTML"))
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<table class="myClassHere">
<tr>
<td>Book Series</td>
</tr>
<tr>
<td>Pern</td>
</tr>
<tr>
<td>Hobbit</td>
</tr>
</table>
<table class="otherClassHere">
<tr>
<td>Movies</td>
</tr>
<tr>
<td>Avengers</td>
</tr>
<tr>
<td>Matrix</td>
</tr>
</table>
I have a table like:
<table id="table">
<tbody>
<tr></tr>
<tr>
<td>
<table>
<tbody>
<tr></tr>
</tbod>
</table>
</td>
</tr>
</tbody>
</table>
I'm using jQuery and there exists a stored selector to get the most outer table:
var x = $('#table')
Starting from that If want to get all first level <tr>-elements.
If I use one of those:
x.find('tbody > tr');
x.children('tbody').children();
… the first one will naturally select all nested <tr>-elements as well. The latter seems over-complicated and involves multiple queries.
Is there a way to make this faster/more efficient?
First thing, x.find('tbody > tr') would find all <tr>s. You would need to do x.find('> tbody > tr'), assuming x is x from your example.
I ran a test and this with both and this was my finding.
.children(): 3.013ms
>: 0.626ms
so the > method is faster than the .children() method. The function calls add up... barely.
Here's my JavaScript for the testing.
var $table = $('#table'), $usingChildren, $usingAngleBracket;
console.time('.children()');
$usingChildren = $table.children('tbody').children('tr');
console.timeEnd('.children()');
console.time('>');
$usingAngleBracket = $table.find('> tbody > tr');
console.timeEnd('>');
console.log( $usingChildren, $usingAngleBracket );
the fastest way to get direct children of a parent is .children, so what you can do is:
$('tbody').children('tr')
.find() will search child of child too, so you may not want to use that.
Use can use jQuery's .first() method to find the first <tr> element,
$('#mytable tr').first()
Although, as you wish to find the first <tr> that has nested child elements, you can filter it with .has(). For example: http://jsfiddle.net/cwL4q/3/
$("#mytable tr").has('tbody').first().css("background-color", "red" );
Although, I would strongly suggest simply labelling the 'nested' <tr>'s with a class, then you can simply access them much quicker as you know.
$('.nestedrow');
For the HTML below:
<table id="table">
<tbody>
<tr></tr>
<tr class="nestedrow">
<td>
<table>
<tbody>
<tr></tr>
</tbod>
</table>
</td>
</tr>
</tbody>
</table>