In a HTML table how can I auto hide an entire row if all the cells (columns) within that row are empty?
I presume there is something I can add to the tag that would do this, but I cannot seem to find a solution anywhere.
In HTML, you can use the hidden attribute, as in <tr hidden>, but this is an HTML5 novelty and has limited browser support. But if you can directly change the HTML markup, the best way to hide an element is to remove it.
Assuming you want something that still lets you have the row there in the markup, for some reason, then you can use JavaScript e.g. as follows:
<script>
function emptyCellsOnly(row) {
var cells = row.cells;
for(var j = 0; j < cells.length; j++) {
if(cells[j].innerHTML !== '') {
return false;
}
}
return true;
}
var rows = document.getElementsByTagName('tr');
for(var i = 0; i < rows.length; i++) {
if(emptyCellsOnly(rows[i])) {
rows[i].style.display = 'none';
}
}
</script>
The test if(cells[j].innerHTML !== '') checks whether cell is completely empty, as in <td></td>. A space character, or a line break, is not counted as empty. If they should be, modify the condition as needed.
The code rows[i].style.display = 'none' hides the row by setting its display property to none, so CSS-enabled browsers will show the page as if the element were not there, but it is still accessible to scripts, etc. You could alternative remove the element completely from the DOM.
You can use a javascript like this
$('tr').each(function() {
if($(this).find('td').length == 0) {
$(this).hide();
}
});
After some research I found this sollution, which has the advantage over empty-cells:hide, that it completely removes the space an empty cell would take up.
<style type="text/css">
table {
border-spacing: 0px; // removes spaces between empty cells
border: 1px solid black;
}
tr, td {
border-style: none; // see note below
padding: 0px; // removes spaces between empty cells
line-height: 2em; // give the text some space inside its cell
height: 0px; // set the size of empty cells to 0
}
</style>
Unfortunately you have to set border-style: none;, else the borders of empty cells will be painted anyway (which results in thick lines).
I tried additional code like:
td:empty {
display: none;
border-style: none;
}
But in my table of 2 columns it removes the borders of either the left or the right column, but never of both...
Any hint at how to remove the borders of empty rows would be appreciated.
in my case cells have Spaces tabs . they are not data.
areAllCellsEmpty : true if, all cells.textContent are empty or Whitespace
innerHTML didnt give the result i wanted.
usage:
hideEmptyRows_ofTableById("myTable_id"); /* <- your table id here */
code:
function isEmptyOrSpaces(str){
return str === null || str.trim() === '' ;
}
function areAllCellsEmpty(row) {
var cells = row.cells;
var anyCellFull = false;
for(var j = 0; j < cells.length; j++) {
if( ! isEmptyOrSpaces(cells[j].textContent) ) {
anyCellFull =true;
break;
}
}
return !anyCellFull;
}
function hideEmptyRows_ofTableById(elem_id){
var table = document.getElementById(elem_id);
var rows = table.getElementsByTagName('tr');
for(var i = 0; i < rows.length; i++) {
if( areAllCellsEmpty(rows[i]) ) {
rows[i].style.display = 'none';
}
}
}
Related
I have a third party library that add texts to my angular app, and I want to style it, unfortunately there is no class or specific element name to do css selector by.
My question is if is it possible to do css selector based on css property.
For example select all elements that are bold
I tried this but doesn't work and I get SassError: Expected identifier
ngx-contentful-rich-text {
line-height: 2rem;
*[font-weight=700] {
margin-top: 2rem;
}
}
You might want to use this:
var paragraphs = document.getElementsByTagName('p');
function change() {
var paragraphs = document.getElementsByTagName('p');
for (i = 0; i < paragraphs.length; i++) {
if (getComputedStyle(paragraphs[i]).fontWeight == 700) {
paragraphs[i].style.backgroundColor = '#00ff00';
}
}
}
Snippet:
function change() {
var paragraphs = document.getElementsByTagName('p');
for (i = 0; i < paragraphs.length; i++) {
if (getComputedStyle(paragraphs[i]).fontWeight == 700) {
paragraphs[i].style.backgroundColor = '#00ff00';
}
}
}
<button onclick="change()">Change!</button>
<p style="font-weight:400;">Will not change.</p>
<p style="font-weight:700;">Will change.</p>
Please can someone guide me on how to implement a static (sticky) header to this dynamically created table?
I have tried multiple things from Stackoverflow threads for a while now but lack HTML/CSS knowledge and I'm obviously missing something simple.
I have managed to get it working using a table created directly in the main body of the code, but when I use my dynamically created tables from JSON I can't get anything to 'stick'.
Below the code:
<!DOCTYPE html>
<html>
<meta name="viewport" content="width=device-width, initial-scale=0.50, maximum-scale=1, user-scalable=0"/>
<head>
<title>iNews HTML Running Order</title>
<style>
table
{
border: solid 1px #CCCCCC;
border-collapse: collapse;
text-align: left;
font:30px Arial;
}
tr, th, td
{
white-space: nowrap;
padding-right: 50px;
}
tr
{
background-color: #ffffff;
border: solid 1px #CCCCCC;
}
th
{
background-color: #CCCCCC;
}
#container
{
text-align: center;
max-width: 100%;
}
</style>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
</head>
<body onload="initialisePage('LW')">
<p id="showData">Loading Running Order...</p>
</body>
<script>
var loop;
var filename;
var table;
function updateJSONData(filename)
{
getDataFromJSON(filename)
loop = setInterval(function(){getDataFromJSON(filename);}, 500);
}
function initialisePage(newFilename)
{
filename = newFilename;
updateJSONData(filename)
}
function setFileName(newFilename)
{
clearInterval(loop)
filename = newFilename;
updateJSONData(filename)
}
function getDataFromJSON(filename)
{
$.get( "http://10.142.32.72/dashboard/"+filename+".json", function( data ) {
var myBooks = JSON.parse(data);
CreateTableFromJSON(myBooks)
});
}
function CreateTableFromJSON(myBooks)
{
var title = ["Page", "Slug", "Pres 1", "Pres 2", "CAM", "Format", "Clip Dur", "Total", "Backtime"];
var col = ["page-number", "title", "pres1", "pres2", "camera", "format", "runs-time", "total-time", "back-time"];
// CREATE DYNAMIC TABLE.
table = document.createElement("table");
// CREATE HTML TABLE HEADER ROW USING THE EXTRACTED HEADERS ABOVE.
var tr = table.insertRow(-1); // TABLE ROW.
for (var i = 0; i < col.length; i++) {
var th = document.createElement("th"); // TABLE HEADER.
th.innerHTML = title[i];
tr.appendChild(th);
}
// ADD JSON DATA TO THE TABLE AS ROWS.
for (var i = 0; i < myBooks.length; i++) {
tr = table.insertRow(-1);
if (myBooks[i]["floated"] == "true"){
tr.style.color = "#ffffff";
tr.style.background = "blue";
}
if ((myBooks[i]["break"] == "true") && (myBooks[i]["floated"] == "false")){
tr.style.background = "#00ff00";
}
for (var j = 0; j < col.length; j++) {
var tabCell = tr.insertCell(-1);
tabCell.innerHTML = myBooks[i][col[j]];
}
}
// FINALLY ADD THE NEWLY CREATED TABLE WITH JSON DATA TO A CONTAINER.
var divContainer = document.getElementById("showData");
divContainer.innerHTML = "";
divContainer.appendChild(table);
console.log("Refreshed: " + filename);
}
</script>
</html>
Many thanks in advance,
Joe
Remove <body onload="initialisePage('LW')"> and use DOMContentLoaded instead as it happens much sooner than the document load event.
load is only fired after ALL resources/content has been loaded, including "non-essential" (non-DOM) content like images and external content like ad-banners, which means the load event may be fired tens-of-seconds after DOMContentLoaded which makes the load event kinda useless today).
Change your CSS to this:
table > thead > tr > th {
position: sticky;
top: 0;
z-index: 10;
}
table > tbody > tr.floated {
color: '#ffffff';
background-color: 'blue';
}
table > tbody > tr.broken {
background-color: '#00ff00';
}
JavaScript uses camelCase for functions, values (variables and parameters) and properties, not PascalCase.
Avoid var and use const and let in scripts where appropriate instead. Note that const means "unchanging reference" (kinda like C++); it does not mean "immutable" or "compile-time constant value". I think this definition of const was a mistake by the JavaScript language designers, but that's just, like, my opinion, man.
Use CSS classes via classList instead of setting individual style properties using .style.
The current JavaScript ecosystem also generally uses 1TBS instead of the Allman style.
Prefer === (exactly-equals) instead of == (equals) because JavaScript's type coercion can be surprising).
Avoid using innerHTML wherever possible. Use .textContent for setting normal text content (and avoid using .innerText too). Misuse of innerHTML leads to XSS vulnerabilities.
It's 2020. STOP USING JQUERY!!!!!!!!!!
Cite
Cite
Cite
Cite
DONT USE ALL-CAPS IN YOUR JAVASCRIPT COMMENTS BECAUSE IT LOOKS LIKE THE AUTHOR IS SHOUTING AT YOU NEEDLESSLY AND IT GETS QUITE ANNOYING FOR OTHER READERS ARRRRGGGHHHHH
You need to handle HTTP request responses correctly (e.g. to check for succesful responses with the correct Content-Type).
Avoid using j as an iterable variable name because it's too visually similar to i.
Change your JavaScript to this:
<script>
// You should put all of your own application-specific top-level page script variables in their own object so you can easily access them separately from the global `window` object.
const myPageState = {
loop : null,
fileName: null,
table : null
};
window.myPageState = myPageState; // In the top-level function, `const` and `let`, unlike `var`, do not create a global property - so you need to explicitly set a property like so: `window.{propertyName} = ...`.
window.addEventListener( 'DOMContentLoaded', onDOMLoaded );
function onDOMLoaded( ev ) {
window.myPageState.fileName = "LW";
window.myPageState.loop = setInterval( refreshTable, 500 );
}
async function refreshTable() {
if( typeof window.myPageState.fileName !== 'string' || window.myPageState.fileName.length === 0 ) return;
const url = "http://10.142.32.72/dashboard/" + window.myPageState.fileName + ".json";
const resp = await fetch( url );
if( resp.status === 200 && resp.headers['ContentType'] === 'application/json' ) {
const deserialized = await resp.json();
ceateAndPopulateTableFromJSONResponse( deserialized );
}
else {
// Error: unexpected response.
// TODO: error handling
// e.g. `console.error` or `throw new Error( "Unexpected response." )`, etc.
}
}
function ceateAndPopulateTableFromJSONResponse( myBooks ) {
// TODO: Verify the `myBooks` object layout (i.e. schema-verify `myBooks`).
const columnTitles = ["Page", "Slug", "Pres 1", "Pres 2", "CAM", "Format", "Clip Dur", "Total", "Backtime"];
const columnNames = ["page-number", "title", "pres1", "pres2", "camera", "format", "runs-time", "total-time", "back-time"];
const table = window.myPageState.table || document.createElement( 'table' );
if( window.myPageState.table !== table ) {
window.myPageState = table;
document.getElementById("showData").appendChild( table );
}
// Create the <thead>, if nnecessary:
if( table.tHead === null )
{
table.tHead = document.createElement( 'thead' );
const tHeadTR = table.tHead.insertRow(-1);
for( let i = 0; i < columnNames.length; i++ ) {
const th = document.createElement('th');
th.textContent = columnTitles[i];
tHeadTR.appendChild( th );
}
}
// Clear any existing tbody:
while( table.tBodies.length > 0 ) {
table.removeChild( table.tBodies[0] );
}
// Populate a new <tbody>:
{
const tbody = document.createElement('tbody');
for( let i = 0; i < myBooks.length; i++ ) {
const tr = table.insertRow(-1);
tr.classList.toggle( 'floated', myBooks[i]["floated"] === "true" );
tr.classList.toggle( 'broken' , myBooks[i]["break" ] === "true" && myBooks[i]["floated"] === "false" );
for( let c = 0; c < columnNames.length; c++ ) {
const td = tr.insertCell(-1);
const colName = columnNames[c];
td.textContent = myBooks[i][ colName ];
}
}
table.appendChild( tbody );
}
console.log( "Refreshed: " + window.myPageState.fileName );
}
</script>
I'm working on an automated slide setup and depending on some opt-out variables I need to remove some of the slides if they are not desired in the final output. To solve this I have created a script that adds a simple text string {{remove-this-slide}} to the slides that need to be deleted.
However, when trying to get a script to delete the slides containing that string it keeps deleting my entire presentation...
This is what I have:
function deleteFunction() {
var currentPresentationSlide = SlidesApp.getActivePresentation().getSlides();
for (i = 0; i < currentPresentationSlide.length; i++) {
if (currentPresentationSlide[i].getPageElements().indexOf('{{remove-this-slide}}') > -1); {
currentPresentationSlide[i].remove();
}
}
}
Can anyone figure out what's going wrong here?
How about this modification?
Modification points :
The reason that the entire slides are deleted is ; after if (currentPresentationSlide[i].getPageElements().indexOf('{{remove-this-slide}}') > -1);. By this ;, if doesn't work and currentPresentationSlide[i].remove(); is always run.
The text data cannot be retrieved from currentPresentationSlide[i].getPageElements(). When you want to search the text from the text box, please use currentPresentationSlide[i].getShapes().
From your question, I was not sure where you want to search the text from. So I supposed that you want to search the text from shapes. The shape includes the text box.
Modified script :
function deleteFunction() {
var currentPresentationSlide = SlidesApp.getActivePresentation().getSlides();
for (i = 0; i < currentPresentationSlide.length; i++) {
var shapes = currentPresentationSlide[i].getShapes();
for (j = 0; j < shapes.length; j++) {
if (shapes[j].getText().asString().indexOf('{{remove-this-slide}}') > -1) {
currentPresentationSlide[i].remove();
}
}
}
}
Reference :
getShapes()
If I misunderstand your question, I'm sorry.
There is a small bug in the code from #Tanaike. Because there might be more shapes in the same slide, you have to break the loop after deleting the slide.
Otherwise the code tries to traverse the shapes of a deleted slide, producing an error.
So the correct snippet looks like:
function deleteFunction() {
var currentPresentationSlide = SlidesApp.getActivePresentation().getSlides();
for (i = 0; i < currentPresentationSlide.length; i++) {
var shapes = currentPresentationSlide[i].getShapes();
for (j = 0; j < shapes.length; j++) {
if (shapes[j].getText().asString().indexOf('{{remove-this-slide}}') > -1) {
currentPresentationSlide[i].remove();
break;
}
}
}
}
I have this function for searching a table and displaying the rows that match my search input
function searchTable() {
var input, filter, found, table, tr, td, i, j;
input = document.getElementById("myInput");
filter = input.value.toUpperCase();
table = document.getElementById("myTable");
tr = table.getElementsByTagName("tr");
for (i = 0; i < tr.length; i++) {
td = tr[i].getElementsByTagName("td");
for (j = 0; j < td.length; j++) {
if (td[j].innerHTML.toUpperCase().indexOf(filter) > -1) {
found = true;
}
}
if (found) {
tr[i].style.display = "";
found = false;
} else {
tr[i].style.display = "none";
}
}
}
I would like to make one of the strings in the data clickable with a hyper link.
However this affects my search because the the link is inside my table data . So even thought it is not visible it is skewing the search results.
What argument could I add to my function to ignore the contents of my bracket and still find the text string in my table. Or how could I place my anchor outside my table data brackets and still have the string be a clickable anchor.
Ex of table row:
<tr>
<td>Abcès au cerveau</td>
<td>Neurologique</td>
<td></td>
</tr>
Sorry beginner question. I am also looking for an argument to highlight the results of my search if anyone has it.
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 8 years ago.
Improve this question
I have a table with the format like this:
TR -> TD rowspan=2 -> TD -> TD
TR -> -> TD -> TD
TR -> TD rowspan=2 -> TD -> TD
TR -> -> TD -> TD
Repeats........
On hover of any TD, the TR is highlighted and the TD with Rowspan is highlighted in full if a TD in either row is highlighted.
Like this:
When moving down the table the CSS is applied / removed properly. However whn moving UP the table the CSS is not removed from the bottom half of the TD with the rowspan
Example:
This only happens in chrome;
Currently my only CSS is
.hover{
background-color: #1797C0 !important;
}
The JS is This:
$("[id$=user_data] td").hover(function() {
$el = $(this);
$el.parent().addClass("hover");
if ($el.parent().has('td[rowspan]').length == 0)
$el
.parent()
.prevAll('tr:has(td[rowspan]):first')
.find('td[rowspan]')
.addClass("hover");
}, function() {
$el
.parent()
.removeClass("hover")
.prevAll('tr:has(td[rowspan]):first')
.find('td[rowspan]')
.removeClass("hover");
});
Is there something in Chrome that causes the color to remain when the TR/TD is no longer hover? Using developer tool the TD does NOT have the class .hover yet the color remains.
So my question is:
Where in my JS is the CSS background color not being reset for the part of the TD that is on the second row of the rowSpan
I would like it to highlight the entire first cell (with rowspan) and the rest of row 1 when hovering on a td in row 1. Highlight the entire TD in Row 1 col 1 when hovering in any TD in row 2
repeat for rows 3-4 and 5-6 and so on, removing the highlight from the previous rows/td(s).
Regardless of moving up or down in the Table
It currently works in Safari and Firefox but Chrome is keeping the Highlight in the bottom of the TD with the rowspan only when moving UP in the table.
I ran into this problem as well. This is my port of that jquery code to native JS.
Hope it helps =)
(function () {
function findRowSpan(element) {
var sibling = element;
var rowspanFound = false;
var rowSpanCols = [];
while (!rowspanFound) {
var cols = sibling.getElementsByTagName("td");
for (var i = 0; i < cols.length; i++) {
var col = cols[i];
if (col.hasAttribute('rowspan')) {
rowSpanCols.push(col);
}
}
rowspanFound = rowSpanCols.length > 0;
sibling = sibling.previousElementSibling;
}
return rowSpanCols;
}
function doForAll(array,func) {
for (var j = 0; j < array.length; j++) {
var item = array[j];
func(item);
}
}
function rowSpanHover(element,func) {
var parent = element.parentNode;
func(parent);
var cols = findRowSpan(parent);
doForAll(cols, function (col) {
func(col);
});
}
function addClass(element,cssClass) {
element.className = element.className +" "+ cssClass;
}
function removeClass(element, cssClass) {
element.className = element.className.replace(cssClass, "").trim();
}
return {
setup: function () {
var cells = document.getElementsByTagName('td');
for (var i = 0; i < cells.length; i++) {
var cell = cells[i];
cell.addEventListener("mouseover", function () {
rowSpanHover(this, function(element) {
addClass(element, "hover");
});
});
cell.addEventListener("mouseout", function () {
rowSpanHover(this, function (element) {
removeClass(element, "hover");
});
});
}
}
};
})();