I have JS function which parses through a table:
// id contains a message id, activeRow is "this" from onClick on tr
function doSomething ( id, activeRow ) {
// AJAX calling using id as parameter
$("#searchResultTable > tbody > tr").each(function(index) {
$(this).removeClass("bold");
});
}
This works perfectly fine (thanks to Ariel # other post), but I was thinking that there should be another possibility, like:
var table = $(activeRow).parent();
$("tbody > tr", table).each(function(index) {
// .. do something
});
// The above clearly doesn't work, but should document what I'm looking for.
This would allow the usage of the same table ID while the function would work on each of them separately.
Many, many thanks!
jQuery's parents() method makes getting the parent table a snap:
$(activeRow).parents('table')[0];
What about:
$(activeRow).siblings().andSelf().removeClass("bold")
That'll take the <tr> in activeRow, grab its sibling <tr>s too, and remove the "bold" class from all of them.
It may be better to use closest as follows:
$(activeRow).closest('table')[0];
From here: http://api.jquery.com/closest/
closest: Travels up the DOM tree until it finds a match for the supplied selector.
parents: Travels up the DOM tree to the document's root element, adding each ancestor element to a temporary collection; it then filters that collection based on a selector if one is supplied.
In this scenario it may well be that parents gets the top most table where there are more than one in the DOM tree, where as closest gets the one you are actually trying to work on.
Close, you would want to use a class on the table
$("table.className tbody tr").each(function() {
// .. do something
});
Related
I need to assert that a <table> element contains both a nested <thead> and a <tbody> element.
Sure, an obvious solution would be sth. like this:
cy.get('table')
.find('thead')
cy.get('table')
.find('tbody')
but isn't there a simple way to to that all in one chain? Like this:
cy.get('table')
.should('contain.element','thead')
.should('contain.element','tbody')
Unfortunatelly there is no such contain.element.
I know, there is .should('contain.html','...') but it doesn't work for me because this would require me to specify the full html string including nested content (with tr, td). I only want to assert that both child elements thead and tbody are existing.
Try jQuery .has()
cy.get('table')
.should($table => {
expect($table.has('thead')).to.eq(true)
expect($table.has('tbody')).to.eq(true)
})
Or same thing in one line
cy.get('table:has(thead):has(tbody)') // pseudo selector :has()
Or same thing in one selector
cy.get('table thead, table tbody') // jQuery Multiple Elements Selector
.should('have.length', 2) // passes if both exist
Update: Okay, I missed your note, related .should('contain.html'..). Usually, contain.html returns true if a node is a descendant of a target node, no matter if it has a deep three child nodes.
You can use should in combination with and. .and() yields the same subject it was given from the previous command:
cy.get('table')
.should('contain.html','thead')
.and('contain.html','tbody')
Another way would be to create an array of elements and using a forEach() loop assert that they are visible, something like this:
const elements = ['table thead', 'table tbody']
elements.forEach((ele) => {
cy.get(ele).should('be.visible')
})
I have table which is being dynamically created.
I would like to try not to have any more attributes in the table (like an ID field).
It is a multilevel table where all the TableRows should be expandable and collapse on click in any of the TD in each row.
$('.fylke_click').click(function () {
$(this).parent().nextUntil('.fylke').slideToggle(0);
$('.sted').hide();
});
$('.kom_click').click(function () {
$(this).parent().nextUntil('.kommune').slideToggle(0);
});
See this simplified fiddle:
http://jsfiddle.net/T2Lwn/
So it's basically 3 levels and it is a lot of problems here.
One obvious one is when you are on the second level, which is called "kommune" and if you click on the last TR it removes the "fylke" underneath. As you can see if you click on "MIDTRE GAULDAL"
This is probably because I use .Parent() and I need some sort of if check if I am on the last row?
Is it also other problems with this code? Can I specify the click method class="fylke_click" and class="kom_click" on a more general level?
For example for all <tr class="fylke"> each TD class will have class="fylke_click" and same for kommunne?
If I understand your issue correctly this may help:
Demo Fiddle:
Since you said you're going to be dynamically creating this content, I would recommend delegating off of the main table instead of making a click handler for each row. Also, since all of the stuff you want to show / hide are siblings and not nested, things get a bit tricky. You'll need to be specific with your .nextUntil() by passing a filter, and I found a :not() on the filter was necessary.
Again, since these are all siblings, it's not as easy as hiding the children of the header row, so I set up an "open" class to check if the header was open or not, and hid / showed stuff depending on if it was already open.
JS:
$('.kommune').hide();
$('.sted').hide();
$('.table').on('click', 'tr', function(){
$this = $(this);
if( $this.hasClass('fylke') ){
if ( $this.hasClass('open') ) {
$this.toggleClass('open').nextUntil('.fylke', 'tr').hide();
}
else {
$this.toggleClass('open').nextUntil('.fylke', 'tr:not(.sted)').toggle();
}
}
else if ( $this.hasClass('kommune') ){
$this.nextUntil('.kommune', 'tr:not(.fylke)').toggle();
}
});
Inside a number of <tr>'s in my document there are 7 classes, each one sometimes has a corresponding class name (i.e. sub, id, assigned, sub, sub, status, and one classless "td"). Within the td class = "status" lies a span class defined as either
<span class = "statusBox">Passed</span>
or
<span class = "statusBox">Failed</span>.
If the text contained within the span class is "Passed", I need to delete the entire <tr>. If it's failed it stays on the document. How can I achieve this?
My first attempt was through a call such as:
function() {
if ($('tr td span.statusBox').html() == "Passed"){
$('tr td span.statusBox').hide();
}
But this removed every instance of only the phrase "Passed", not the entire <tr>.
I've also tried
$("tr td span.statusBox:contains('Passed')").each(function(){
$(this).css("visibility","hidden");
});
I feel like this is more along the right path, but I can't seem to get it to work.
You were close: find the status box with the word 'Passed' and then find the closest ancestor tr element and remove it from the DOM:
$("tr td span.statusBox").filter(":contains('Passed')").each(function(){
$(this).closest('tr').remove();
});
Since the :contains operator is a jQuery extension (it has been deprecated from the CSS spec), it's faster to break the selector into two parts. The first is finding all spans of class statusBox, this will look up the elements using native browser methods. Then those elements are filtered using the :contains operator (which may have a native implementation or may be implemented in jQuery, depending on the browser).
It is because you are altering the visibility of the span instead of the parent <tr>. You can use $(this).closest('tr') to get to the row element and then alter its visibility.
$("tr td span.statusBox:contains('Passed')").each(function(){
$(this).closest('tr').css("visibility","hidden");
});
$(".statusBox").each(function (i, e) {
if ($(e).text() == "Passed") {
$(e).closest('tr').hide();
}
});
http://jsfiddle.net/B7Wqy/
My code looks like this, in closeup:
<h2>
<span class="stuff">[<a id="someid">stuff</a>]</span> <span class="moreStuff">Another test</span>
</h2>
I've found a way to select my a element, and attach an id to it. What I need to do now is select its parent <h2> element, but not the <span> element. How can I do that (JQuery allowed)?
Edit: when I retrieve the selected <a>s, I get an array of them (there's lots of these structures on my page). When I try to write myArray[someIndex].closest("h2"), it says that the element does not have a closest() method. How would I go about this?
One ways is to use the .parents() method of jQuery, with a selector. Something like this.
$("#someid").parents("h2");
Update:
You can use the .closest() method with a selector, to only get the closest parent that match the selector.
$("#someid").closest("h2");
Update 2:
It would be a bit more work to do it with plain JavaScript. Not sure if it is the most efficient, but one way would be to select the element with document.getElementById() and then get a reference to its parent through the parentNode property. Then you would have to check if it is an h2 element, and if not, look at that elements parent node, and so on.
You could check the jQuery source and see how they have implemented the closest method.
I just needed the same thing. here a vanilla javascript variant:
function findParent(startElement, tagName) {
let currentElm = startElement;
while (currentElm != document.body) {
if (currentElm.tagName.toLowerCase() == tagName.toLowerCase()) { return currentElm; }
currentElm = currentElm.parentElement;
}
return false;
}
The <h2> is not the parent of the <a> but it is an ancestor, use .closest() to select it
$("#someid").closest("h2");
try use .parent() for get exactly double or more level up the DOM tree.
$("#someid").parent().parent();
I am using the following code to remove an element from the DOM tree:
function onItemDeleted(name) {
$("#" + name).remove();
}
Would this be bad for performance since I am not indicating any parent for the element. The element is a TR contained in a TABLE element. The DOM search for this element will start at the top which might be BODY. So would it be something like this:
BODY => DIV => TABLE => TR (found)
If I find the parent of TR which is TABLE would the search be like this:
TABLE -> TR
I don't know if above will be true since I think search will always start at the root node.
jQuery optimises for ID searches. So, $("#" + name) is effectively the same as $(document.getElementById(name)). From the source, line 120 (1.4):
// HANDLE: $("#id")
} else {
elem = document.getElementById( match[2] );
The difference in performance would likely be negligible.
I guess that when you find elements by ID, the lookup time should be O(1) since there can be only one element with that ID.