Empresas
Empleos
  • Sobre nosotros
  • Soluciones
    • Publicación de vacantes
      Publica tu vacante y recibe candidatos calificados en 48h.
    • Evaluación de candidatos
      500+ pruebas técnicas y psicológicas, más anti-fraude.
    • Headhunting
      Búsqueda ejecutiva a la medida de principio a fin.
    • Nómina + EOR
      Dispersión de nómina y EOR en más de 15 países de LATAM.
  • Precios
  • Empleos

0

372
Vistas
Ordenar una tabla en JavaScript: ocultar filas de tablas sin filas posteriores

Tengo una tabla que construí en JavaScript que es básicamente una gran lista de publicaciones, con un encabezado de "año", seguido de una fila para cada publicación de ese año:

 <table class="mytable" id="myTable"> <tr class="header"> <th style="width:60%;">2020</th> </tr> <tr name="latin_america_zero-deforestation"> <td>"Cattle Ranchers and Deforestation.” </td> </tr> <tr name="latin_america_policy-outcomes"> <td>“Impacts on Land Use Conversion.” </td> </tr> <tr name="latin_america_supply-chain_policy-outcomes"> <td>“Costs of the Amazon Soy Moratorium.” </td> </tr> <tr class="header"> <th style="width:60%;">2019</th> </tr> <tr name="africa_policy-outcomes"> <td>“Environmental Change” </td> </tr> <tr name="latin_america_policy-outcomes"> <td>“Land Use Change” </td> </tr> <tr class="header"> <th style="width:60%;">2018</th> </tr> <tr name="north_america_zero-deforestation"> <td>“Deforestation Remedies” </td> </tr> <tr name="latin_america_zero-deforestation"> <td>“Land Use Change in Latin American Cerrados” </td> </tr> </table>

Según el nombre de tr , puedo filtrar por palabras clave (ya sea región o tema) a través de algunos botones:

 <button class="btn" onclick="filterSelection('policy-outcomes')">Policy Outcomes</button> <button class="btn" onclick="filterSelection('latin_america')"> Latin America</button>

Los botones llaman a esta función, que filtra según las palabras clave que se encuentran en la etiqueta de nombre tr :

 //Sort by class.. function filterSelectionByTopic(thing) { var rows = $('#myTable').find('tr:not(:has(th))').get(); console.log(rows) $("#myTable tr").show() $('#myTable').find('tr:not(:has(th))').not('thead td').not("[name*=" + thing + "]").fadeOut(300) var tabhead = "header" //$("#myTable tbody tr:not(."+thing + ')').hide(); //$("#myTable tr.header").fadeIn(1) }

Si bien esto funciona bien, quiero que los filtros oculten los encabezados de las tablas (años) donde no se devuelven filas.

Por ejemplo, si filtré usando filterSelection('policy-outcomes') : 2020 y 2019 tienen publicaciones que cumplen con este criterio, 2018 no. Actualmente, 2018 se mostrará como un encabezado de tabla sin filas debajo, pero me gustaría ocultar cualquier encabezado de tabla que no tenga filas que cumplan con los criterios.

Entonces, en esencia, filterSelection('policy-outcomes') debería verse así:

 2020 Impacts on Land Use Conversion. Costs of the Amazon Soy Moratorium. 2019 Environmental Change. Land Use Change.

¿Cómo puedo lograr esto?

over 4 years ago · Santiago Trujillo
6 Respuestas
Responde la pregunta

0

Puede combinar el selector :has con el selector hermano adyacente ( s1 + s2 ).
Así que haz algo como:

 $("#myTable").find("tr.header:has(+ tr.header)").hide()

Lo que seleccionaría cada fila de encabezado seguida inmediatamente por otra fila de encabezado, es decir, cada encabezado que no tenga al menos una fila asociada.

y supongo que uso algo como :last-child para capturar el caso de borde donde el último encabezado no devuelve filas.

over 4 years ago · Santiago Trujillo Denunciar

0

Esta solución comienza en la última fila y sube por el árbol DOM. Si una fila de la tabla no tiene un término, lo oculta. También agregué un botón claro.

 function filterSelection(topic) { const index = {}; const HIDE_CLASS = 'policy-no-term'; let tr = document.querySelector('tr:last-child'); let hasTerms = false; while(tr) { tr.classList.remove(HIDE_CLASS); if (tr.classList.contains('header')) { if (hasTerms === false) { tr.classList.add(HIDE_CLASS); } hasTerms = false; } else if(tr.getAttribute('name').indexOf(topic) > -1) { hasTerms = true; tr.classList.remove(HIDE_CLASS); } else { hasTerms = hasTerms || false; tr.classList.add(HIDE_CLASS); } tr = tr.previousElementSibling; } }
over 4 years ago · Santiago Trujillo Denunciar

0

Solución

Esta solución pasa por cada fila y guarda los encabezados. Luego busca las filas de datos que le siguen que coincidan con la expresión regular del término de búsqueda. Si no hubo filas de datos después del encabezado antes de que llegue al siguiente encabezado, se aplicará un estilo de ninguno al nodo del encabezado.

Ejemplo

El siguiente ejemplo establece un término de búsqueda de áfrica y luego, 4 segundos más tarde, aplica un término de búsqueda de latín.

 //Sort by class.. function filterSelectionByTopic(thing) { $("#myTable tr").show() $('#myTable').find('tr:not(:has(th))').not('thead td').not("[name*=" + thing + "]").fadeOut(300) const allRows = Array.from($('#myTable').find('tr')); let currentHeaderNode = null; let visibleRows = 0; allRows.forEach((row, index,{ length })=>{ // reset display none from last search if (row.classList.contains("header")) row.style.display=undefined; if (row.classList.contains("header") || index===length-1) { if (!visibleRows && currentHeaderNode !== null) { currentHeaderNode.style.display="none"; } // header to target if no sibling data rows currentHeaderNode = row; visibleRows = 0; return } if (new RegExp(thing,"gi").test(row.getAttribute("name"))) { // There IS a row that will be shown (don't hide header) visibleRows++; } }) } filterSelectionByTopic("africa") setTimeout(()=>{filterSelectionByTopic("latin")},4000)
 th { background-color:red; }
 <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> <table class="mytable" id="myTable"> <tr class="header"> <th style="width:60%;">2020</th> </tr> <tr name="latin_america_zero-deforestation"> <td>"Cattle Ranchers and Deforestation.” </td> </tr> <tr name="latin_america_policy-outcomes"> <td>“Impacts on Land Use Conversion.” </td> </tr> <tr name="latin_america_supply-chain_policy-outcomes"> <td>“Costs of the Amazon Soy Moratorium.” </td> </tr> <tr class="header"> <th style="width:60%;">2019</th> </tr> <tr name="africa_policy-outcomes"> <td>“Environmental Change” </td> </tr> <tr name="latin_america_policy-outcomes"> <td>“Land Use Change” </td> </tr> <tr class="header"> <th style="width:60%;">2018</th> </tr> <tr name="north_america_zero-deforestation"> <td>“Deforestation Remedies” </td> </tr> <tr name="latin_america_zero-deforestation"> <td>“Land Use Change in Latin American Cerrados” </td> </tr> </table>

over 4 years ago · Santiago Trujillo Denunciar

0

Esta es mi versión de Vanilla JavaScript.

Antes de escribir el script, cambié todos los atributos de name a data-name ya que tr s no puede tener atributos de name "legalmente".

En un primer bucle filter() , hago invisibles todos los tr s y filtro solo aquellos tr s que no coinciden con el patrón pat y no son de clase "encabezado". En un bucle forEach() aplicado en este conjunto filtrado, hago visibles solo aquellos tr s que tienen un atributo dataset.name o cuyo sucesor directo tiene este atributo.

 const trs=[...document.querySelector("#myTable>tbody").children]; function fltTbl(pat){ trs .filter(tr=>(tr.style.display="none",tr.classList.contains("header")|| tr.dataset.name&&tr.dataset.name.indexOf(pat)>-1)) .forEach((tr,i,a)=>{ if (tr.dataset.name || a[i+1]&&a[i+1].dataset.name) tr.style.display=""; }); }
 <button class="btn" onclick="fltTbl('policy-outcomes')">Policy Outcomes</button> <button class="btn" onclick="fltTbl('latin_america')">Latin America</button> <button class="btn" onclick="fltTbl('deforestation')">Deforestation</button> <table class="mytable" id="myTable"> <tr class="header"> <th style="width:60%;">2020</th> </tr> <tr data-name="latin_america_zero-deforestation"> <td>"Cattle Ranchers and Deforestation.” </td> </tr> <tr data-name="latin_america_policy-outcomes"> <td>“Impacts on Land Use Conversion.” </td> </tr> <tr data-name="latin_america_supply-chain_policy-outcomes"> <td>“Costs of the Amazon Soy Moratorium.” </td> </tr> <tr class="header"> <th style="width:60%;">2019</th> </tr> <tr data-name="africa_policy-outcomes"> <td>“Environmental Change” </td> </tr> <tr data-name="latin_america_policy-outcomes"> <td>“Land Use Change” </td> </tr> <tr class="header"> <th style="width:60%;">2018</th> </tr> <tr data-name="north_america_zero-deforestation"> <td>“Deforestation Remedies” </td> </tr> <tr data-name="latin_america_zero-deforestation"> <td>“Land Use Change in Latin American Cerrados” </td> </tr> </table>

over 4 years ago · Santiago Trujillo Denunciar

0

Por lo general , no es una buena idea usar controladores de eventos en línea. El fragmento usa delegación de eventos y es-20xx sin formato.

Los botones contienen un atributo de data-term partir del cual se puede construir un selector de consulta css para las filas data-name . Los atributos de name se reemplazan por atributos data-name datos, con valores delimitados por comas, correspondientes a los atributos de data-term de datos de los botones.

A las filas de 'año' agregué un atributo data-year datos, para poder encontrar fácilmente un encabezado de una fila data-name .

El manejador:

  • Filtra todas las filas usando una expresión regular derivada del data-term -atributo del botón en el que se hizo clic
  • Si se encuentra un elemento, lo muestra, encuentra el encabezado anterior y lo muestra también

El fragmento también está disponible en Stackblitz .

 const doFilter = FilterHandlerFactory(); document.addEventListener(`click`, handle); function handle(evt) { if (evt.target.dataset.term) { return doFilter(evt.target); } } // Factory to avoid recreation of [showParentHeader] on every click function FilterHandlerFactory() { const isHead = r => r.dataset.year && r; // helper (closed over): // move up until a header is found and display that const showParentHeader = row => { let prev = row.previousElementSibling; while (prev && !isHead(prev)) { prev = prev.previousElementSibling; } prev.classList.remove(`hidden`); }; return fromBtn => { const all = fromBtn.dataset.term === `All`; const filter = RegExp(fromBtn.dataset.term, "i"); document.querySelectorAll(`#myTable tr`) .forEach( row => { if (all || filter.test(row.dataset.name)) { row.classList.remove(`hidden`); return all || showParentHeader(row); } row.classList.add(`hidden`); } ); } }
 body { font: normal 12px/15px verdana, arial; } #myTable { border: 1px solid #999; border-collapse: separate; margin-top: 1rem; width: 80vw; } #myTable th { background-color: #ddd; border-bottom: 1px dotted #777; border-top: 1px dotted #777; } button { margin-right: 2px; } button[data-term]:before { content: attr(data-term); } .hidden { display: none; } .btnBlock { display: flex; flex-direction: row; }
 <div class="btnBlock"> <button data-term="Policy Outcomes"></button> <button data-term="Latin America"></button> <button data-term="Deforestation"></button> <button data-term="Supply Chain"></button> <button data-term="Africa"></button> <button data-term="North America"></button> <button data-term="All"></button> </div> <table class="mytable" id="myTable"> <tr class="header" data-year="2020"> <th>2020</th> </tr> <tr data-name="latin america, deforestation"> <td>"Cattle Ranchers and Deforestation.”</td> </tr> <tr data-name="latin america, policy outcomes"> <td>“Impacts on Land Use Conversion.”</td> </tr> <tr data-name="latin america, supply chain, policy outcomes"> <td>“Costs of the Amazon Soy Moratorium.”</td> </tr> <tr class="header" data-year="2019"> <th>2019</th> </tr> <tr data-name="africa, policy outcomes"> <td>“Environmental Change”</td> </tr> <tr data-name="latin america, policy outcomes"> <td>“Land Use Change”</td> </tr> <tr class="header" data-year="2018"> <th>2018</th> </tr> <tr data-name="north america, deforestation"> <td>“Deforestation Remedies”</td> </tr> <tr data-name="latin america, deforestation"> <td>“Land Use Change in Latin American Cerrados”</td> </tr> </table>

Alternativamente, considere no usar una <table> (después de todo, los datos no son realmente tabulares).

Patio de juegos Stackblitz para este fragmento.

 document.addEventListener(`click`, handle); const doFilter = FilterHandlerFactory(`myData`); function handle(evt) { if (evt.target.dataset.term) { return doFilter(evt.target); } } // filter handler factory function FilterHandlerFactory(filterId) { let filterTerm, all; const filterYears = document.querySelectorAll(`#${filterId} div[data-year]`); const filterItems = document.querySelectorAll(`#${filterId} ul li`); const year = item => item.closest(`[data-year]`); const hide = el => el.classList.add(`hidden`); const show = el => el.classList.remove(`hidden`); const displayFilteredItem = item => { show(item); show(year(item)); }; const setTerms = fromBtn => { all = fromBtn.dataset.term === `All`; filterTerm = RegExp(fromBtn.dataset.term, "i"); }; const filter = item => all || filterTerm.test(item.dataset.name || '-') ? displayFilteredItem(item) : hide(item); return fromBtn => { setTerms(fromBtn); filterYears.forEach( hide ) filterItems.forEach( filter ); }; }
 body { margin: 2rem; font: normal 12px/15px verdana, arial; } #myData { margin-top: 1rem; width: 80vw; } #myData div[data-year] { opacity: 1; max-height: initial; transition: all ease-in-out .7s; } #myData div[data-year].hidden { opacity: 0; max-height: 0; margin: 0; } #myData div[data-year] ul li.hidden { display: none; } button { margin-right: 2px; } button[data-term]:before { content: attr(data-term); } .btnBlock { display: flex; flex-direction: row; } #myData div[data-year] ul { margin-left: -1.5rem; margin-top: 0.2rem; } #myData div[data-year] ul li { list-style-type: '\2713'; padding-left: 5px; } #myData div[data-year]:before { content: attr(data-year); text-align: center; font-weight: bold; display: block; width: 100%; background-color: #ddd; border-bottom: 1px dotted #777; border-top: 1px dotted #777; }
 <div class="btnBlock"> <button data-term="Policy Outcomes"></button> <button data-term="Latin America"></button> <button data-term="Deforestation"></button> <button data-term="Supply Chain"></button> <button data-term="Africa"></button> <button data-term="North America"></button> <button data-term="All"></button> </div> <div id="myData"> <div data-year="2020"> <ul> <li data-name="latin america, deforestation"> Cattle Ranchers and Deforestation. </li> <li data-name="latin america, policy outcomes"> Impacts on Land Use Conversion. </li> <li data-name="latin america, supply chain, policy outcomes"> Costs of the Amazon Soy Moratorium. </li> </ul> </div> <div data-year="2019"> <ul> <li data-name="africa, policy outcomes">Environmental Change</li> <li data-name="latin america, policy outcomes">Land Use Change</li> </ul> </div> <div data-year="2018"> <ul> <li data-name="north america, deforestation"> Deforestation Remedies </li> <li data-name="latin america, deforestation"> Land Use Change in Latin American Cerrados </li> </ul> </div> </div>

over 4 years ago · Santiago Trujillo Denunciar

0

Puede combinar el selector :has con el selector hermano adyacente (s1 + s2). Así que haz algo como:

 $("#myTable").find("tr.header:has(+ tr.header)").hide()
over 4 years ago · Santiago Trujillo Denunciar
Responde la pregunta
Encuentra empleos remotos

¡Descubre la nueva forma de encontrar empleo!

Top de empleos
Top categorías de empleo
Empresas
Publicar vacante Precios Comercial
Legal
Términos y condiciones Política de privacidad
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomiéndame algunas ofertas
Necesito ayuda