Good morning,
I'm hoping someone can help me with this seemingly simple question. I can't figure out why my textContent or innerHTML won't update my DOM. It shows in my console that it has changed, but for some reason that escapes me I can't figure out why the DOM isn't changing. Any help is appreciated!
I have the following code
function updateText() {
document.querySelectorAll('.userRoleStyle').forEach(function (e) {
var grabtext = e.textContent || e.innerHTML;
if (grabtext === 'SUPER') {
grabtext = 'Super User';
}
console.log(grabtext);
})
};
table td {
padding: 10px;
}
<body>
<table>
<thead>
<tr>
<th>Username</th>
<th>Email address</th>
<th>User Role</th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td>TEST1</td>
<td>test#test.com</td>
<td class="userRoleStyle">ADMINISTRATOR</td>
<td>
Test
</td>
</tr>
<tr class="userList">
<td>TEST2</td>
<td>test#test.com</td>
<td class="userRoleStyle">ADMINISTRATOR</td>
<td>
Test
</td>
</tr>
<tr class="userList">
<td>TEST3</td>
<td>test#test.com</td>
<td class="userRoleStyle">SUPER</td>
<td>
Test
</td>
</tr>
</tbody>
</table>
</body>
innerHTML and textContent are basically setters and getters. Getting their reference in a variable and setting value to that reference will not invoke the setter.
You need to set value directly to them
e.textContent ?
( e.textContent == 'SUPER' ? (e.textContent = 'Super User') : "" ) :
( e.innerHTML == 'SUPER' ? (e.innerHTML = 'Super User') : "" )
I have updated the snippet.
You were not assigning the Super User value to e.textContent inside if condition
function updateText() {
document.querySelectorAll('.userRoleStyle').forEach(function (e) {
var grabtext = e.textContent || e.innerHTML;
if (grabtext === 'SUPER') {
e.textContent = 'Super User';
}
})
};
table td {
padding: 10px;
}
<body>
<table>
<thead>
<tr>
<th>Username</th>
<th>Email address</th>
<th>User Role</th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td>TEST1</td>
<td>test#test.com</td>
<td class="userRoleStyle">ADMINISTRATOR</td>
<td>
Test
</td>
</tr>
<tr class="userList">
<td>TEST2</td>
<td>test#test.com</td>
<td class="userRoleStyle">ADMINISTRATOR</td>
<td>
Test
</td>
</tr>
<tr class="userList">
<td>TEST3</td>
<td>test#test.com</td>
<td class="userRoleStyle">SUPER</td>
<td>
Test
</td>
</tr>
</tbody>
</table>
</body>
Hope this will help
Nothing in your code changes what's in the DOM. Assigning to your grabText variable just changes the value of that variable, not the element you got the text from.
To do that, you'd havE to assign back to textContent/innerHTML.
Separately: innerHTML is very different from textContent. If you're going to feature-detect, use textContent and fall back to innerText, not innerHTML¹. When feature-detecting this, don't just use ||, it can pick the wrong one if the element's text is blank. Instead, look to see if the element has a property with the desired name. You only need to do it once and remember it.
var textPropName = "textContent" in document.createElement("div") ? "textContent" : "innerText";
function updateText() {
document.querySelectorAll('.userRoleStyle').forEach(function(e) {
if (e[textPropName] === 'SUPER') {
e[textPropName] = 'Super User';
}
})
}
var textPropName = "textContent" in document.createElement("div") ? "textContent" : "innerText";
function updateText() {
document.querySelectorAll('.userRoleStyle').forEach(function(e) {
if (e[textPropName] === 'SUPER') {
e[textPropName] = 'Super User';
}
})
}
table td {
padding: 10px;
}
<body>
<table>
<thead>
<tr>
<th>Username</th>
<th>Email address</th>
<th>User Role</th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td>TEST1</td>
<td>test#test.com</td>
<td class="userRoleStyle">ADMINISTRATOR</td>
<td>
Test
</td>
</tr>
<tr class="userList">
<td>TEST2</td>
<td>test#test.com</td>
<td class="userRoleStyle">ADMINISTRATOR</td>
<td>
Test
</td>
</tr>
<tr class="userList">
<td>TEST3</td>
<td>test#test.com</td>
<td class="userRoleStyle">SUPER</td>
<td>
Test
</td>
</tr>
</tbody>
</table>
</body>
Side note: You don't put ; after function declarations. It's a statement terminator; declarations aren't statements. You'd have one after an assignment statement with a function expression, but not after a declaration. (It's harmless, though.)
¹ textContent and innerText are also different from one another, but the differences don't matter in your case.
Related
I have an HTML table and I need to define a function that should grab the data from the table and build an array of objects that contains table data. Outside the function I have to declare a variable and assign the returned value from the function.
Thanks in advance.
HTML
<table>
<thead>
<tr>
<th>Name</th>
<th>Rating</th>
<th>Review</th>
</tr>
</thead>
<tbody>
<tr>
<td>Bob</td>
<td>5/5</td>
<td>This product is so good, I bought 5 more!</td>
</tr>
<tr>
<td>Jane</td>
<td>4/5</td>
<td>Good value for the price.</td>
</tr>
<tr>
<td>David</td>
<td>1/5</td>
<td>Arrived broken :(</td>
</tr>
<tr>
<td>Fiona</td>
<td>5/5</td>
<td>I love it!</td>
</tr>
<tr>
<td>Michael</td>
<td>3/5</td>
<td>Doesn't live up to expectations.</td>
</tr>
</tbody>
</table>
JS
function buildTableData() {
let tbody = document.getElementsByTagName("tbody")[0];
let rows = tbody.children;
let people = [];
for (let row of rows) {
let person = {};
let cells = row.children;
person.rating = cells[0].textContent;
person.review = cells[1].textContent;
person.favoriteFood = cells[2].textContent;
people.push(person);
return people;
}
let data = people;
console.log(data);
}
You can get all the elements by using querySelectorAll('td'). Then use map to to get only the text of it and return this.
function buildTableData() {
const elements = [...document.querySelectorAll('td')];
return elements.map(x => {
return {content : x.innerHTML}
});
}
console.log(buildTableData());
<body>
<h2>Product reviews</h2>
<table>
<thead>
<tr>
<th>Name</th>
<th>Rating</th>
<th>Review</th>
</tr>
</thead>
<tbody>
<tr>
<td>Bob</td>
<td>5/5</td>
<td>This product is so good, I bought 5 more!</td>
</tr>
<tr>
<td>Jane</td>
<td>4/5</td>
<td>Good value for the price.</td>
</tr>
<tr>
<td>David</td>
<td>1/5</td>
<td>Arrived broken :(</td>
</tr>
<tr>
<td>Fiona</td>
<td>5/5</td>
<td>I love it!</td>
</tr>
<tr>
<td>Michael</td>
<td>3/5</td>
<td>Doesn't live up to expectations.</td>
</tr>
</tbody>
</table>
<script src="https://cdnjs.cloudflare.com/ajax/libs/acorn/7.3.1/acorn.js" integrity="sha512-4GRq4mhgV43mQBgKMBRG9GbneAGisNSqz6DSgiBYsYRTjq2ggGt29Dk5thHHJu38Er7wByX/EZoG+0OcxI5upg==" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/acorn-walk/7.2.0/walk.js" integrity="sha512-j5XDYQOKluxz1i4c7YMMXvjLLw38YFu12kKGYlr2+w/XZLV5Vg2R/VUbhN//K/V6LPKuoOA4pfcPXB5NgV7Gwg==" crossorigin="anonymous"></script>
<script src="index.js"></script>
</body>
You can try using querySelectorAll() and map() like the following way:
function buildTableData() {
let rows = document.querySelectorAll('tbody tr');
let data = Array.from(rows).map(function(tr){
return {
rating: tr.querySelectorAll('td:nth-child(1)')[0].textContent,
review: tr.querySelectorAll('td:nth-child(2)')[0].textContent,
favoriteFood: tr.querySelectorAll('td:nth-child(3)')[0].textContent
};
});
console.log(data);
}
buildTableData();
<h2>Product reviews</h2>
<table>
<thead>
<tr>
<th>Name</th>
<th>Rating</th>
<th>Review</th>
</tr>
</thead>
<tbody>
<tr>
<td>Bob</td>
<td>5/5</td>
<td>This product is so good, I bought 5 more!</td>
</tr>
<tr>
<td>Jane</td>
<td>4/5</td>
<td>Good value for the price.</td>
</tr>
<tr>
<td>David</td>
<td>1/5</td>
<td>Arrived broken :(</td>
</tr>
<tr>
<td>Fiona</td>
<td>5/5</td>
<td>I love it!</td>
</tr>
<tr>
<td>Michael</td>
<td>3/5</td>
<td>Doesn't live up to expectations.</td>
</tr>
</tbody>
</table>
You want a loop, and each review to be an object that is appended to an array of reviews is what I'm assuming
var reviews = [];
var tbody = document.querySelectorAll("tbody")[0];
var TRs = tbody.querySelectorAll("tr");
for (var a = 0; a < TRs.length; a++) {
var TDs = TRs[a].querySelectorAll("td");
var review = {
name: "",
rating: "",
review: ""
};
//These assume the order of your table columns don't change
review.name = TDs[0].innerHTML;
review.rating = TDs[1].innerHTML;
review.review = TDs[2].innerHTML;
reviews.push(review);
}
Your reviews array should have everything in there just as you wanted. I assumed the third column was "review" instead of "favorite food"
I have a table like below
<table id="categoriesTable">
<tr id=row_id1_dynamicdata>
<td>...</td>
<td>..</td>
</tr>
<tr id=row_id2_dynamicdata>
<td>...</td>
<td>..</td>
</tr>
<tr id=row_id3_dynamicdata>
<td>...</td>
<td>..</td>
</tr>
<tr id=row_id4_dynamicdata>
<td>...</td>
<td>..</td>
</tr>
</table>
I want to hide all rows except row whose id contains id4. I won't have full id.
I came up with below jQuery code, but as I don't have full id, it doesn't work.
var idValue = document.getElementById(someElement);
$('#categoreisTable').find('tr').not($('#row_' +idValue)).hide();
How to filter with only half the id?
You can use the "Attribute starts with" selector to find the rows which don't match the one with the specified idValue. For example:
$('#someElement').on('change', function() {
var idValue = this.value;
$('#categoriesTable')
.find('tr')
.show() // not needed if you only want to hide
.not('[id^="row_id' + idValue + '_"]')
.hide();
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<table id="categoriesTable">
<tr id=row_id1_dynamicdata>
<td>.1..</td>
<td>..</td>
</tr>
<tr id=row_id2_dynamicdata>
<td>.2..</td>
<td>..</td>
</tr>
<tr id=row_id3_dynamicdata>
<td>.3..</td>
<td>..</td>
</tr>
<tr id=row_id4_dynamicdata>
<td>.4..</td>
<td>..</td>
</tr>
</table>
<input type="text" id="someElement" />
You can use querySelectorall on the tr element and then run a loop to only show rows that include id4 in their id.
Run the snippet below:
var idValue = document.querySelectorAll('tr');
for (i = 0; i < idValue.length; i++) {
if (idValue[i].id.includes("id4")) {
idValue[i].style.display = "block";
} else {
idValue[i].style.display = "none"
}
}
<table id="categoriesTable">
<tr id=row_id1_dynamicdata>
<td>row1</td>
<td>..</td>
</tr>
<tr id=row_id2_dynamicdata>
<td>row2</td>
<td>..</td>
</tr>
<tr id=row_id3_dynamicdata>
<td>row3</td>
<td>..</td>
</tr>
<tr id=row_id4_dynamicdata>
<td>row 4</td>
<td>..</td>
</tr>
<tr id=anotherrow_id4_dynamicdata>
<td>another row with id4</td>
<td>..</td>
</tr>
</table>
You can use document.getElementsByTagName to get all id contain id4.
Then, just to hide them.
let ElementArray = Array.prototype.filter.call(document.getElementsByTagName('tr'), element => element.id.match('id4'));
let idArray = ElementArray.forEach(element => document.getElementById(element.id).style.display="none");
You can simply use:
$('#categoriesTable tr').not('[id^="row_id4_"]').hide();
table:
<table id=tblList>
<thead>
<th>Firstname</th>
<th>Lastname</th>
<th>Status</th>
<th>Action</th>
</thead>
<tbody>
<tr>
<td>Jane</td>
<td>Doe</td>
<td>Pending</td>
<td><button class="cancelThis">Cancel</button></td>
</tr>
<tr>
<td>John</td>
<td>Doe</td>
<td>Cancelled</td>
<td><button class="cancelThis">Cancel</button></td>
</tr>
</tbody>
</table>
and script
var search = 'Cancelled';
$('#tblAppointment tr td').filter(function () {
return $(this).text() == search;
}).parent('tr').css('color', 'red');
with code above, i've managed to changed the color of the row that contains "Cancelled" to red
and with this:
$('#tblAppointment tr td').filter(function () {
return $(this).text() == search;
}).find(".cancelThis").prop("disabled", true);
is not working.
should be: for every row that contains string "Cancelled" cancel button will be disabled. rows that doesn't contain string "Cancelled" will remain unaffected.
TIA.
You can just add the same logic as the one that is working, Add .parent('tr') before .find(".cancelThis").prop("disabled", true);
$('#tblAppointment tr td').filter(function() {
return $(this).text() == search;
}).parent('tr').find(".cancelThis").prop("disabled", true);
You can cut down you code a bit,
var search = 'Cancelled';
var t = $('#tblAppointment tr td').filter(function() {
return $(this).text() == search;
}).parent('tr');
t.css('color', 'red');
t.find(".cancelThis").prop("disabled", true);
Demo
var search = 'Cancelled';
$('#tblAppointment tr td').filter(function() {
return $(this).text() == search;
}).parent('tr').css('color', 'red');
$('#tblAppointment tr td').filter(function() {
return $(this).text() == search;
}).parent('tr').find(".cancelThis").prop("disabled", true);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<table id=tblAppointment>
<thead>
<th>Firstname</th>
<th>Lastname</th>
<th>Status</th>
<th>Action</th>
</thead>
<tbody>
<tr>
<td>Jane</td>
<td>Doe</td>
<td>Pending</td>
<td><button class="cancelThis">Cancel</button></td>
</tr>
<tr>
<td>John</td>
<td>Doe</td>
<td>Cancelled</td>
<td><button class="cancelThis">Cancel</button></td>
</tr>
</tbody>
</table>
Your initial query is getting a list of <td> elements. So when you call find on that, you're only searching inside the <td> (which doesn't have a .cancelThis) element. You'd have to do something like this:
$('#tblAppointment tr td').filter(function () {
return $(this).text() == search;
}).parent('tr').find(".cancelThis").prop("disabled", true);
However, I can think of one issue you might want to resolve. What if First or Last named is "Cancelled"? Your query would match that too. If you add a class to the status <td>, you can search for it specifically:
<table id='tblAppointment'>
<thead>
<th>Firstname</th>
<th>Lastname</th>
<th>Status</th>
<th>Action</th>
</thead>
<tbody>
<tr>
<td>Jane</td>
<td>Doe</td>
<td class='status'>Pending</td>
<td><button class="cancelThis">Cancel</button></td>
</tr>
<tr>
<td>John</td>
<td>Doe</td>
<td class='status'>Cancelled</td>
<td><button class="cancelThis">Cancel</button></td>
</tr>
</tbody>
</table>
$('#tblAppointment tr td.status').filter(function () {
return $(this).text() == search;
}).parent('tr').find(".cancelThis").prop("disabled", true);
This will only look at the text of the td.status elements, and not the other elements (like first and last name).
Is it possible to examine the content within a tr, AFTER an html element (br) to see if any exists? If there is no content after the br element, I'd like to hide the parent td. Please note that the html code is system generated and I cannot edit it.
I'm just not sure where to begin with this. Any help is greatly appreciated.
<table class="tabledefault">
<tbody>
<tr>
<td id="customfields">
<table class="tabledefault">
<tbody>
<tr><!-- this TR should be hidden -->
<td id="CAT_Custom_451068"><strong>Laser Tag</strong>
<br>
</td>
</tr>
<tr>
<td id="CAT_Custom_451069"><strong>Arcade</strong>
<br>Selected
</td>
</tr>
<tr>
<td id="CAT_Custom_450908"><strong>Bounce House (45 minutes) $100</strong>
<br>False
</td>
</tr>
<tr>
<td id="CAT_Custom_451307"><strong>Party Room Rental (per hour) $75</strong>
<br>True</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
Try using .each() , nextSibling , nodeValue , String.prototype.match() , .closest()
$("table tr td br").each(function(i, el) {
// if `br` next sibling does not contain alphanumeric characters,
// hide parent `tr` element
if (el.nextSibling.nodeType === 3
&& el.nextSibling.nodeValue.match(/\w+/) === null
|| $(el).next(":empty").length) {
$(this).closest("tr").hide()
}
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js">
</script>
<table class="tabledefault">
<tbody>
<tr>
<td id="customfields">
<table class="tabledefault">
<tbody>
<tr><!-- this TR should be hidden -->
<td id="CAT_Custom_451068"><strong>Laser Tag</strong>
<br><span></span>
</td>
</tr>
<tr>
<td id="CAT_Custom_451069"><strong>Arcade</strong>
<br>Selected
</td>
</tr>
<tr>
<td id="CAT_Custom_450908"><strong>Bounce House (45 minutes) $100</strong>
<br>False
</td>
</tr>
<tr>
<td id="CAT_Custom_451307"><strong>Party Room Rental (per hour) $75</strong>
<br>True</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
Yes, you just get the trs, then find out if the first <br> element inside the first <td> has any following element siblings (I'm making an assumption there, that you don't want those hidden), or any following text node siblings that aren't blank. jQuery's contents is handy for that, as it includes text nodes. I'd probably loop through them backward:
$("#customfields .tabledefault tr").each(function(index) {
var $tr = $(this);
$tr.find("td:first").contents().get().reverse().some(function(node) {
if (node.nodeName.toUpperCase() === "BR") {
// Hide it, and we're done looping
$tr.hide();
return true;
}
if (node.nodeType != 3 || $.trim(node.nodeValue)) {
// Don't hide it, and we're done looping
return true;
}
});
});
I expect that can be optimized, but you get the idea.
Live Example:
var counter = 3;
tick();
function tick() {
$("#countdown").text(counter--);
if (counter < 0) {
hideIt();
} else {
setTimeout(tick, 500);
}
}
function hideIt() {
$("#customfields .tabledefault tr").each(function(index) {
var $tr = $(this);
$tr.find("td:first").contents().get().reverse().some(function(node) {
if (node.nodeName.toUpperCase() === "BR") {
// Hide it, and we're done looping
$tr.hide();
return true;
}
if (node.nodeType != 3 || $.trim(node.nodeValue)) {
// Don't hide it, and we're done looping
return true;
}
});
});
}
<table class="tabledefault">
<tbody>
<tr>
<td id="customfields">
<table class="tabledefault">
<tbody>
<tr>
<!-- this TR should be hidden -->
<td id="CAT_Custom_451068"><strong>Laser Tag</strong>
<br>
</td>
</tr>
<tr>
<td id="CAT_Custom_451069"><strong>Arcade</strong>
<br>Selected
</td>
</tr>
<tr>
<td id="CAT_Custom_450908"><strong>Bounce House (45 minutes) $100</strong>
<br>False
</td>
</tr>
<tr>
<td id="CAT_Custom_451307"><strong>Party Room Rental (per hour) $75</strong>
<br>True</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div id="countdown"> </div>
I want to hide all of the <tr> where td's text is 0. How can I do that? I have to mention that in reality i have more than 600 rows. But the example below is a demo. THX
<table id ="list2">
<tbody>
<tr>
<td>2</td>
<td>213</td>
<td id ="hideRow">0</td>
<tr>
<tr>
<td>vb</td>
<td>asf</td>
<td id ="hideRow">0</td>
<tr>
<tr>
<td>cxvb</td>
<td>xcvb</td>
<td id ="hideRow">2</td>
<tr>
<tr>
<td>cas</td>
<td>asdf</td>
<td id ="hideRow">45</td>
<tr>
</tbody>
</table>
This is my try :| . The event is loaded by onclick event
$('#list2').find("tr td #hideRow").each(function(){
var txt2 = $(this).text();
if (txt2 =="0"){
$('#list2').find("tr").each(function(){
$(this).hide();
});
}
})
First of all do not use id for duplicate names. Try doing it like following.
<table id ="list2">
<tbody>
<tr>
<td>2</td>
<td>213</td>
<td class="hideRow">0</td>
<tr>
<tr>
<td>vb</td>
<td>asf</td>
<td class="hideRow">0</td>
<tr>
<tr>
<td>cxvb</td>
<td>xcvb</td>
<td class="hideRow">2</td>
<tr>
<tr>
<td>cas</td>
<td>asdf</td>
<td class="hideRow">45</td>
<tr>
</tbody>
</table>
$('#list2').find(".hideRow").each(function(){
var txt2 = $(this).text();
if (txt2 =="0"){
$(this).parent().hide();
}
})
IDs on elements need to be unique, you can't have multiple <td id="hideRow"> elements and expect things to play nicely all of the time. I'd suggest changing it to a class. Then, select all elements:
var elems = $('span.hideRow');
Filter to those whose text is 0:
elems = elems.filter(function() {
return $(this).text() === "0";
});
Get their parent <tr> element:
elems = elems.closest('tr');
Then, finally, hide them:
elems.hide();
That can, obviously, all be done in one line:
$('span.hideRow').filter(function() {return $(this).text() === "0";}).closest('tr').hide();