3
votes

Fusionner les lignes du tableau à l'aide de JavaScript

J'utilise une table normale, voici mon exigence:

Si les valeurs d'une ligne de table sont dupliquées, elle devrait être fusionnée en utilisant javascript.Par exemple: les valeurs de ligne de table 1 et les valeurs de ligne de table2 sont identiques, elles devraient être fusionnées et la valeur ne doit être affichée qu'une seule fois en utilisant javascript. Ici, je vais attacher mes codages.

<body onload="myFunction()">
  <table>
    <tr>
      <th>Company</th>
      <th>Contact</th>
      <th>Country</th>
    </tr>
    <tbody>
      <tr>
        <td>Alfreds Futterkiste</td>
        <td>Maria Anders</td>
        <td>Germanyindgo</td>
      </tr>
      <tr>
        <td>Alfreds Futterkiste</td>
        <td>Maria Anders</td>
        <td>Germany</td>
      </tr>
      <tr>
        <td>Centro comercial Moctezuma</td>
        <td>Francisco Chang</td>
        <td>Mexico</td>
      </tr>
      <tr>
        <td>Ernst Handel</td>
        <td>Roland Mendel</td>
        <td>Austria</td>
      </tr>
      <tr>
        <td>Island Trading</td>
        <td>Helen Bennett</td>
        <td>UK</td>
      </tr>
      <tr>
        <td>Laughing Bacchus Winecellars</td>
        <td>Yoshi Tannamuri</td>
        <td>Canada</td>
      </tr>
      <tr>
        <td>Magazzini Alimentari Riuniti</td>
        <td>Giovanni Rovelli</td>
        <td>Italy</td>
      </tr>
    </tbody>
  </table>
</body>
table {
  font-family: arial, sans-serif;
  border-collapse: collapse;
  width: 100%;
}

th {
  background: #a9a9a9;
}

td,
th {
  border: 1px solid #dddddd;
  text-align: center;
  padding: 8px;
  font-family: monospace;
  font-size: 17px;
}

.dark {
  background-color: #dddddd;
}
function myFunction() {
  const firstRows = { };
  let shade = false;
  
  const colsToMerge = [0, 1];

  Array.from(document.querySelectorAll('tbody tr')).forEach(tr => {
    const text = tr.children[0].innerText;
    if (!(text in firstRows)) {
      firstRows[text] = { shade, elem: tr, count: 1 };
      shade = !shade;
    } else {
      const firstRow = firstRows[text]
      firstRow.count++;
      colsToMerge.forEach(i => tr.children[i].remove());
      colsToMerge.forEach(i =>
        firstRow.elem.children[i]
                .setAttribute('rowspan', firstRow.count)
      );
    }
    if (firstRows[text].shade) tr.classList.add('dark');
  });
}

Consultez ce tableau


3 commentaires

Quel est le problème ici? Il semble que ce doublon Alfreds soit déjà supprimé?


Vous pouvez utiliser l'attribut add "rowspan" dans vos cellules pour fusionner des lignes similaires. Juste comme ça: jsfiddle.net/two8kq1n


@adiga; @Marcelo Macedo Je souhaite définir la plage de lignes de manière dynamique. Par exemple: Prenons les deux premières lignes si la première colonne est la même, elle devrait être fusionnée dynamiquement au lieu de la donner comme statique.


3 Réponses :


1
votes

vous pouvez le faire dynamiquement par Javascript. Vérifiez ce violon:

let previousObj = [];
const rows = $("table tr");
rows.each(function(i, el) {
    let obj = [];
    $(el).children("td").each(function(ic, elc) {
    obj.push(elc);

    if (previousObj.length > ic) {
        if (previousObj[ic].innerHTML == obj[ic].innerHTML) {
        $(previousObj[ic]).attr('rowspan', getRowsSpan(ic, i, obj[ic].innerHTML));
        $(obj[ic]).remove();
      }      
    }
  });

  previousObj = obj;
});

function getRowsSpan(col, row, value) {
    var rowSpan = 2;
  var actualRow = row+1;

  while ($(rows[actualRow]).children("td")[col].innerHTML == value) {
    rowSpan++;
    actualRow++;
  } 

  return rowSpan;
}

https://jsfiddle.net/9e1bcprx/ 2 /


3 commentaires

Voulez-vous cela sans jQuery?


L'envergure de lignes peut être supérieure à 2


Désolé, pour ce bug. J'ai créé une fonction pour rechercher le rowSpan, ici: jsfiddle.net/9e1bcprx/2



2
votes

Voici une solution de travail pour fusionner dynamiquement des cellules de ligne consécutives dans chaque colonne.

La solution fonctionne également pour basculer la nuance et ne changera la nuance que lorsque une ligne «complète» commence, c'est-à-dire lorsqu'il n'y a pas de fusion entre des lignes consécutives.

De plus, vous devez envelopper le tr dans l'en-tête du tableau dans un thead car cela peut poser des problèmes avec document.querySelectorAll ('tbody tr') event tbody est spécifié dans la requête.

I légèrement modifié le tableau pour afficher l'ombrage et la fusion: entrez la description de l'image ici

<body onload="myFunction()">
  <table>
    <thead>
      <tr>
        <th>Company</th>
        <th>Contact</th>
        <th>Country</th>
        <th>Col4</th>
        <th>Col5</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td>Alfreds Futterkiste</td>
        <td>Maria Anders</td>
        <td>Germanyindgo</td>
        <td>1</td>
        <td>2</td>
      </tr>
      <tr>
        <td>Alfreds Futterkiste</td>
        <td>Maria Anders</td>
        <td>Germany</td>
        <td>1</td>
        <td>2</td>
      </tr>
      <tr>
        <td>Centro comercial Moctezuma</td>
        <td>Francisco Chang</td>
        <td>Mexico</td>
        <td>1</td>
        <td>2</td>
      </tr>
      <tr>
        <td>Ernst Handel</td>
        <td>Francisco Chang</td>
        <td>Austria</td>
        <td>1</td>
        <td>2</td>
      </tr>
      <tr>
        <td>Ernst Handel</td>
        <td>Helen Bennett</td>
        <td>UK</td>
        <td>1</td>
        <td>2</td>
      </tr>
      <tr>
        <td>Laughing Bacchus Winecellars</td>
        <td>Yoshi Tannamuri</td>
        <td>Canada</td>
        <td>1</td>
        <td>2</td>
      </tr>
      <tr>
        <td>Magazzini Alimentari Riuniti 1</td>
        <td>Giovanni Rovelli 1</td>
        <td>Italy</td>
        <td>1</td>
        <td>2</td>
      </tr>
      <tr>
        <td>Magazzini Alimentari Riuniti 1</td>
        <td>Giovanni Rovelli 2</td>
        <td>Italy</td>
        <td>1</td>
        <td>2</td>
      </tr>
      <tr>
        <td>Magazzini Alimentari Riuniti 3</td>
        <td>Giovanni Rovelli 3</td>
        <td>Italy</td>
        <td>1</td>
        <td>2</td>
      </tr>
    </tbody>
  </table>
</body>
table {
  font-family: arial, sans-serif;
  border-collapse: collapse;
  width: 100%;
}

th {
  background: #a9a9a9;
}

td,
th {
  border: 1px solid black;
  text-align: center;
  padding: 8px;
  font-family: monospace;
  font-size: 17px;
}

.dark {
  background-color: #dddddd;
}
function myFunction() {
  const previousRow = {};
  const colsChanged = {};
  let leftMerged = false;
  let dark = false;

  Array.from(document.querySelectorAll('tbody tr')).forEach((tr, rowIdx) => {
    Array.from(tr.children).forEach((td, colIdx) => {
      if (rowIdx > 0 && (colIdx === 0 || leftMerged) && previousRow[colIdx].text === td.innerText) {
        previousRow[colIdx].elem.setAttribute('rowspan', ++previousRow[colIdx].span);
        colsChanged[colIdx] = false;
        td.remove();
        if (colIdx === 0) {
          leftMerged = true;
        }
      } else {
        previousRow[colIdx] = { span: 1, text: td.innerText, elem: td, dark };
        colsChanged[colIdx] = true;
      }
    });
    const rowChanged = Object.values(colsChanged).every(Boolean);
    dark = rowChanged && rowIdx > 0 ? !dark : dark;
    if (dark) {
      tr.classList.add('dark');
    }
    leftMerged = false;
  });
}

D'après les commentaires ci-dessous, si vous souhaitez fusionner les cellules vers la droite uniquement si les cellules correspondantes ont été fusionnées dans la première colonne , voici une modification pour le faire.

une variable leftMerged est utilisée pour savoir si la première cellule de la ligne courante a été fusionnée avec la précédente, et si c'est le cas, cela nous permet de fusionner les cellules suivantes pour le droit si nécessaire. La variable est réinitialisée à false au début de chaque ligne.

entrez la description de l'image ici

<body onload="myFunction()">
  <table>
    <thead>
      <tr>
        <th>Company</th>
        <th>Contact</th>
        <th>Country</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td>Alfreds Futterkiste</td>
        <td>Maria Anders</td>
        <td>Germanyindgo</td>
      </tr>
      <tr>
        <td>Alfreds Futterkiste</td>
        <td>Maria Anders</td>
        <td>Germany</td>
      </tr>
      <tr>
        <td>Centro comercial Moctezuma</td>
        <td>Francisco Chang</td>
        <td>Mexico</td>
      </tr>
      <tr>
        <td>Ernst Handel</td>
        <td>Francisco Chang</td>
        <td>Austria</td>
      </tr>
      <tr>
        <td>Ernst Handel</td>
        <td>Helen Bennett</td>
        <td>UK</td>
      </tr>
      <tr>
        <td>Laughing Bacchus Winecellars</td>
        <td>Yoshi Tannamuri</td>
        <td>Canada</td>
      </tr>
      <tr>
        <td>Magazzini Alimentari Riuniti 1</td>
        <td>Giovanni Rovelli 1</td>
        <td>Italy</td>
      </tr>
      <tr>
        <td>Magazzini Alimentari Riuniti 2</td>
        <td>Giovanni Rovelli 2</td>
        <td>Italy</td>
      </tr>
      <tr>
        <td>Magazzini Alimentari Riuniti 3</td>
        <td>Giovanni Rovelli 3</td>
        <td>Italy</td>
      </tr>
    </tbody>
  </table>
</body>
table {
  font-family: arial, sans-serif;
  border-collapse: collapse;
  width: 100%;
}

th {
  background: #a9a9a9;
}

td,
th {
  border: 1px solid black;
  text-align: center;
  padding: 8px;
  font-family: monospace;
  font-size: 17px;
}

.dark {
  background-color: #dddddd;
}
function myFunction() {
  const previousRow = {};
  const colsChanged = {};
  let dark = false;

  Array.from(document.querySelectorAll('tbody tr')).forEach((tr, rowIdx) => {
    Array.from(tr.children).forEach((td, colIdx) => {
      if (rowIdx > 0 && previousRow[colIdx].text === td.innerText) {
        previousRow[colIdx].elem.setAttribute('rowspan', ++previousRow[colIdx].span);
        colsChanged[colIdx] = false;
        td.remove();
      } else {
        previousRow[colIdx] = { span: 1, text: td.innerText, elem: td, dark };
        colsChanged[colIdx] = true;
      }
    });
    const rowChanged = Object.values(colsChanged).every(Boolean);
    dark = rowChanged && rowIdx > 0 ? !dark : dark;
    if (dark) {
      tr.classList.add('dark');
    }
  });
}


13 commentaires

jo_va je veux fusionner pour les trois premières colonnes


je veux fusionner pour les trois premières colonnes si la première colonne est la même


@Thirshal, je suis désolé, je ne comprends pas ce que vous voulez dire, dans votre question, vous voulez fusionner des cellules similaires entre les lignes, ce que fait ce code, ainsi que gérer l'ombrage. Pouvez-vous m'expliquer à nouveau ce que vous voulez?


Voici mon exigence: 1) Les deux premières lignes de la première colonne seule, puis les deux premières lignes de la première colonne seule doivent être fusionnées. 2) Si la première colonne de la troisième et de la quatrième ligne n'est pas la même, elle devrait être fusionnée, si la troisième colonne est la même, elle ne devrait pas être fusionnée, car la première colonne de la troisième et de la quatrième ligne n'est pas la même. 3) Ensuite, la couleur doit être en blanc et gris, si la même ligne est dupliquée, elle doit être de la même couleur que la ligne parent


Vous souhaitez donc fusionner les cellules des colonnes de droite uniquement si les cellules à leur gauche ont également été fusionnées, est-ce que je comprends bien?


oui si la première colonne des deux lignes est la même @jo_va


continuons cette discussion dans le chat .


Si j'ajoute deux colonnes supplémentaires, la fusion ne fonctionne pas pour ces deux colonnes. @ Jo_va


@Thirshal, j'ai corrigé le deuxième extrait de code, j'ai eu une erreur avec leftMerged = colIdx === 0, je l'ai enveloppé dans une instruction if pour ne le définir true que pour la première colonne, au lieu de le réaffecter


merci, @jo_va J'ai aussi essayé, si la condition que je viens d'itérer leftmerged ++ cela fonctionne pour toute la table


comment puis-je appliquer la fusion uniquement pour les deux premières colonnes et les deux dernières colonnes. Merci d'avance @jo_va


@Thirshal, belle question, pourriez-vous la publier comme question distincte?


Publiera ça @jo_va



1
votes

Dans l'extrait ci-dessous, je lis d'abord les données de la table vers le tableau js, puis je fusionne les lignes avec la première colonne similaire et je génère un nouveau contenu html du corps de la table

<body onload="myFunction()">
  <table>
    <tr>
      <th>Company</th>
      <th>Contact</th>
      <th>Country</th>
    </tr>
    <tbody>
      <tr>
        <td>Alfreds Futterkiste</td>
        <td>Maria Anders</td>
        <td>Germanyindgo</td>
      </tr>
      <tr>
        <td>Alfreds Futterkiste</td>
        <td>Maria Anders</td>
        <td>Germany</td>
      </tr>
      <tr>
        <td>Centro comercial Moctezuma</td>
        <td>Francisco Chang</td>
        <td>Mexico</td>
      </tr>
      <tr>
        <td>Ernst Handel</td>
        <td>Roland Mendel</td>
        <td>Austria</td>
      </tr>
      <tr>
        <td>Island Trading</td>
        <td>Helen Bennett</td>
        <td>UK</td>
      </tr>
      <tr>
        <td>Laughing Bacchus Winecellars</td>
        <td>Yoshi Tannamuri</td>
        <td>Canada</td>
      </tr>
      <tr>
        <td>Magazzini Alimentari Riuniti</td>
        <td>Giovanni Rovelli</td>
        <td>Italy</td>
      </tr>
    </tbody>
  </table>
</body>
table {
  font-family: arial, sans-serif;
  border-collapse: collapse;
  width: 100%;
}

th {
  background: #a9a9a9;
}

td,
th {
  border: 1px solid #dddddd;
  text-align: center;
  padding: 8px;
  font-family: monospace;
  font-size: 17px;
}

.dark {
  background-color: #dddddd;
}
let readTable = (tbody) => [...tbody.rows].map(row=>[...row.cells].map(c=>c.innerText));    

function mergeRows(data) {
  let h={};
  data.forEach((r,i)=> { // merge by first column
      let p=h[r[0]];
      if(p) { p[1].push(r[1]); p[2].push(r[2]) };
      if(!p) { h[r[0]]=[r[0],[r[1]],[r[2]],i]}         
  });     
  let result=Object.values(h).sort((a,b)=>a[3]-b[3]);  
  result.forEach(r=> {r[1]=[... new Set(r[1])]; r[2]=[... new Set(r[2])]}); // remove duplicates in seconond and third column
  return result;
}

function genTable(rows, tbody) {    
  let b="",x="";
  rows.forEach(r=> {
    x=r.map((c,i) => `<td>${c}</td>`).slice(0,3);
    b+=`<tr>${x.join('')}</tr>`;
  })
  tbody.innerHTML=b;
  console.log({b,rows, tbody});
}

function myFunction() {
  let table=document.querySelector('table');
  let tbody=table.tBodies[table.tBodies.length-1]; 
  genTable( mergeRows(readTable(tbody)), tbody );
}


2 commentaires

Si la première ligne et la première colonne de la deuxième ligne sont identiques, la colonne seule doit fusionner, et si la première ligne et la deuxième ligne sont différentes, mais que la colonne 3 seule est la même, elle ne doit pas être fusionnée. la deuxième ligne de la première colonne est "Alfreds", alors la colonne seule doit fusionner. Si la première ligne et la première colonne de la deuxième ligne sont "alfred" et "centro", elles ne devraient pas être fusionnées, même si sa troisième colonne est la même.


@Thirshal J'ai fait un test - et cela fonctionne comme vous le décrivez - si la dernière colonne a des valeurs similaires pour de nombreuses lignes, alors ces lignes ne seront PAS fusionnées. Les lignes ne seront fusionnées que si les valeurs de la PREMIÈRE colonne sont identiques.