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
Related
I have the following table:
<h1>Selected Shakespear Plays</h1>
<table>
<thead>
<tr>
<th>Title</th>
<th>Category</th>
</tr>
</thead>
<tbody>
<tr>
<td>As You Like It</td>
<td>Comedy</td>
</tr>
<tr>
<td>All's Well that Ends Well</td>
<td>Comedy</td>
</tr>
<tr>
<td>Henry V</td>
<td>History</td>
</tr>
</tbody>
</table>
And then the below script that uses XPath expressions. I believe that when the book I am following was published, XPath was built into jQuery, but it has since been moved out into the jquery-xpath plugin.
$("tr:not([th]):odd").addClass("odd");
$("tr:not([th]):even").addClass("even");
$("td:contains('Henry')").addClass("highlight");
$("thead tr").addClass("table-heading").removeClass("even");
This is intended to add the classes odd and even to all tr elements that don't contain a th element, i.e. to all rows except the first, header row. It does not work, the header row, even with th elements still gets the class even. This is why I had to add .removeClass("even"); to the last line of code.
Is the XPath query not([th]) or just [th]? Then, how do I translate this jQuery "XPath statement":
$("tr:not([th]):odd").addClass("odd");
to a standard jQuery statement that fetches all tr that don't contain any th?
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 question. Is there any possibility in jQuery dataTables plugin to only filtering rows with specific class?
For example: I got a table and I want to filter only rows with searchable class. The rest rows stays as they is.
Is this even possible?
<table class="dataTable">
<tr class="searchable">
<td>text to search</td>
<td>text to search</td>
</tr>
<tr>
<td>something else</td>
<td>something else</td>
</tr>
<tr class="searchable">
<td>text to search</td>
<td>text to search</td>
</tr>
<tr>
<td>something else</td>
<td>something else</td>
</tr>
</table>
Edit:
I'm thinking to write my own plugin, but I never do that before.
Something like:
getFilterInput
table.each(tr).function(){
if(row.hasClass(searchable){
//do filtering
}else{
//leave row alone
}
Anyone does something like that and can give me a clue where to start?
You can just use selector like this: $('.searchable')
$('tr').not('.searchable').hide();
One thing you could do is use a hidden column that says whether that row is searchable or not, then when you need to find those specific rows, you just search that particular column.
The way you're trying to leverage classes in your markup will almost certainly require that you modify DataTables.js and/or develop your own plugin.
I have a table that kind of expands and collapses, but it's getting too messy to use it and IE and Firefox are not working properly with it.
So, here's the JavaScript code:
function toggle_it(itemID){
// Toggle visibility between none and ''
if ((document.getElementById(itemID).style.display == 'none')) {
document.getElementById(itemID).style.display = ''
event.preventDefault()
} else {
document.getElementById(itemID).style.display = 'none';
event.preventDefault()
}
}
And a Sample HTML:
<table>
<tr>
<td>Product</td>
<td>Price</td>
<td>Destination</td>
<td>Updated on</td>
</tr>
<tr>
<td>Oranges</td>
<td>100</td>
<td>+ On Store</td>
<td>22/10</td>
</tr>
<tr id="tr1" style="display:none">
<td></td>
<td>120</td>
<td>City 1</td>
<td>22/10</td>
</tr>
<tr id="tr2" style="display:none">
<td></td>
<td>140</td>
<td>City 2</td>
<td>22/10</td>
</tr>
<tr>
<td>Apples</td>
<td>100</td>
<td>+ On Store</td>
<td>22/10</td>
</tr>
<tr id="tr3" style="display:none">
<td></td>
<td>120</td>
<td>City 1</td>
<td>22/10</td>
</tr>
<tr id="tr4" style="display:none">
<td></td>
<td>140</td>
<td>City 2</td>
<td>22/10</td>
</tr>
</table>
The problem is that I use one ID for each and every and that's very annoying because I want to have a lot of hidden rows for each parent and a lot of parents, so it would be too many IDs to handle. And IE and FireFox are only showing the first Hidden Row and not the others. I suspect this happens because I've made it work by triggering all IDs together.
I think it would be better if I use Classes instead of IDs to indetify the hidden rows.
I'm really new to all of this so please try and explaining it in any kind of simply way. Also I've tried jQuery but wasn't able to get it.
It's difficult to figure out what you're trying to do with this sample but you're actually on the right track thinking about using classes. I've created a JSFiddle to help demonstrate a slightly better way (I hope) of doing this.
Here's the fiddle: link.
What you do is, instead of working with IDs, you work with classes. In your code sample, there are Oranges and Apples. I treat them as product categories (as I don't really know what your purpose is), with their own ids. So, I mark the product <tr>s with class="cat1" or class="cat2".
I also mark the links with a simple .toggler class. It's not good practice to have onclick attributes on elements themselves. You should 'bind' the events on page load using JavaScript. I do this using jQuery.
$(".toggler").click(function(e){
// you handle the event here
});
With this format, you are binding an event handler to the click event of links with class toggler. In my code, I add a data-prod-cat attribute to the toggler links to specify which product rows they should control. (The reason for my using a data-* attribute is explained here. You can Google 'html5 data attributes' for more information.)
In the event handler, I do this:
$('.cat'+$(this).attr('data-prod-cat')).toggle();
With this code, I'm actually trying to create a selector like $('.cat1') so I can select rows for a specific product category, and change their visibility. I use $(this).attr('data-prod-cat') this to access the data-prod-cat attribute of the link the user clicks. I use the jQuery toggle function, so that I don't have to write logic like if visible, then hide element, else make it visible like you do in your JS code. jQuery deals with that. The toggle function does what it says and toggles the visibility of the specified element(s).
I hope this was explanatory enough.
Well one way to do it would be to just put a class on the "parent" rows and remove all the ids and inline onclick attributes:
<table id="products">
<thead>
<tr>
<th>Product</th>
<th>Price</th>
<th>Destination</th>
<th>Updated on</th>
</tr>
</thead>
<tbody>
<tr class="parent">
<td>Oranges</td>
<td>100</td>
<td>+ On Store</td>
<td>22/10</td>
</tr>
<tr>
<td></td>
<td>120</td>
<td>City 1</td>
<td>22/10</td>
</tr>
<tr>
<td></td>
<td>140</td>
<td>City 2</td>
<td>22/10</td>
</tr>
...etc.
</tbody>
</table>
And then have some CSS that hides all non-parents:
tbody tr {
display : none; // default is hidden
}
tr.parent {
display : table-row; // parents are shown
}
tr.open {
display : table-row; // class to be given to "open" child rows
}
That greatly simplifies your html. Note that I've added <thead> and <tbody> to your markup to make it easy to hide data rows and ignore heading rows.
With jQuery you can then simply do this:
// when an anchor in the table is clicked
$("#products").on("click","a",function(e) {
// prevent default behaviour
e.preventDefault();
// find all the following TR elements up to the next "parent"
// and toggle their "open" class
$(this).closest("tr").nextUntil(".parent").toggleClass("open");
});
Demo: http://jsfiddle.net/CBLWS/1/
Or, to implement something like that in plain JavaScript, perhaps something like the following:
document.getElementById("products").addEventListener("click", function(e) {
// if clicked item is an anchor
if (e.target.tagName === "A") {
e.preventDefault();
// get reference to anchor's parent TR
var row = e.target.parentNode.parentNode;
// loop through all of the following TRs until the next parent is found
while ((row = nextTr(row)) && !/\bparent\b/.test(row.className))
toggle_it(row);
}
});
function nextTr(row) {
// find next sibling that is an element (skip text nodes, etc.)
while ((row = row.nextSibling) && row.nodeType != 1);
return row;
}
function toggle_it(item){
if (/\bopen\b/.test(item.className)) // if item already has the class
item.className = item.className.replace(/\bopen\b/," "); // remove it
else // otherwise
item.className += " open"; // add it
}
Demo: http://jsfiddle.net/CBLWS/
Either way, put the JavaScript in a <script> element that is at the end of the body, so that it runs after the table has been parsed.
JQuery 10.1.2 has a nice show and hide functions that encapsulate the behavior you are talking about. This would save you having to write a new function or keep track of css classes.
$("tr1").show();
$("tr1").hide();
w3cSchool link to JQuery show and hide
event.preventDefault()
Doesn't work in all browsers. Instead you could return false in OnClick event.
onClick="toggle_it('tr1');toggle_it('tr2'); return false;">
Not sure if this is the best way, but I tested in IE, FF and Chrome and its working fine.
Below is my Script which show/hide table row with id "agencyrow".
<script type="text/javascript">
function showhiderow() {
if (document.getElementById("<%=RadioButton1.ClientID %>").checked == true) {
document.getElementById("agencyrow").style.display = '';
} else {
document.getElementById("agencyrow").style.display = 'none';
}
}
</script>
Just call function showhiderow()upon radiobutton onClick event
AngularJS directives ng-show, ng-hide allows to display and hide a row:
<tr ng-show="rw.isExpanded">
</tr>
A row will be visible when rw.isExpanded == true and hidden when
rw.isExpanded == false.
ng-hide performs the same task but requires inverse condition.
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>