Coding Study of the Day!
Source Code (T A B L E) of "dry and hot RACE" POST

<div class="ff-container">

  <div class="ff-panel">


    <!-- SEARCH -->

    <input 

      type="text" 

      id="ffSearch" 

      class="ff-search" 

      placeholder="🔍 Search..."

      onkeyup="applyFilters()"

    >


    <!-- FILTER CHIPS -->

    <div class="ff-chips" id="chipContainer">

      <div class="chip" data-filter="fwd">FWD</div>

      <div class="chip" data-filter="rwd">RWD</div>

      <div class="chip" data-filter="awd">AWD</div>

      <div class="chip" data-filter="turbo">Turbo</div>

      <div class="chip" data-filter="street">Street</div>

      <div class="chip" data-filter="motorsport">Motorsport</div>

      <div class="chip" data-filter="cheap">Budget</div>

      <div class="chip" data-filter="performance">Performance</div>

    </div>


    <!-- TABLE -->

    <div style="overflow-x:auto;">

      <table class="ff-table" id="ffTable">

        <thead>

          <tr>

            <th onclick="sortTable(0, this)">Jetta Mk3 <span class="arrow">⇅</span></th>

            <th onclick="sortTable(1, this)">Sierra Sapphire <span class="arrow">⇅</span></th>

          </tr>

        </thead>

        <tbody>

          <tr data-tags="fwd street cheap">

            <td>Compact tuner sedan</td>

            <td>Mid-size performance sedan</td>

          </tr>

          <tr data-tags="fwd">

            <td>Front-Wheel Drive (FWD)</td>

            <td>Rear-Wheel Drive / AWD</td>

          </tr>

          <tr data-tags="turbo performance">

            <td>Modified inline-4</td>

            <td>2.0L turbocharged Cosworth</td>

          </tr>

          <tr data-tags="performance">

            <td>~250–400+ hp</td>

            <td>~204–220 hp</td>

          </tr>

          <tr data-tags="street">

            <td>Street racing style</td>

            <td>Motorsport heritage</td>

          </tr>

          <tr data-tags="street">

            <td>Flashy decals</td>

            <td>Sleeper look</td>

          </tr>

          <tr data-tags="cheap">

            <td>Cheaper mods</td>

            <td>Higher performance ceiling</td>

          </tr>

        </tbody>

      </table>

    </div>


  </div>

</div>


<script>

let sortState = {};

let activeFilters = new Set();


/* CHIP CLICK (MULTI-SELECT) */

document.querySelectorAll(".chip").forEach(chip => {

  chip.addEventListener("click", () => {

    const value = chip.dataset.filter;

    chip.classList.toggle("active");


    if (activeFilters.has(value)) {

      activeFilters.delete(value);

    } else {

      activeFilters.add(value);

    }


    applyFilters();

  });

});


/* SORT */

function sortTable(colIndex, header) {

  const table = document.getElementById("ffTable");

  const tbody = table.querySelector("tbody");

  const rows = Array.from(tbody.rows);

  const headers = table.querySelectorAll("th");


  headers.forEach(th => th.querySelector(".arrow").textContent = "⇅");


  sortState[colIndex] = !sortState[colIndex];

  const direction = sortState[colIndex];


  header.querySelector(".arrow").textContent = direction ? "↑" : "↓";


  rows.sort((a, b) => {

    let A = a.cells[colIndex].innerText.toLowerCase();

    let B = b.cells[colIndex].innerText.toLowerCase();

    return direction

      ? A.localeCompare(B, undefined, {numeric:true})

      : B.localeCompare(A, undefined, {numeric:true});

  });


  rows.forEach(row => tbody.appendChild(row));

}


/* FILTER */

function applyFilters() {

  const search = document.getElementById("ffSearch").value.toLowerCase();

  const rows = document.querySelectorAll("#ffTable tbody tr");


  rows.forEach(row => {

    const text = row.innerText.toLowerCase();

    const tags = row.dataset.tags || "";


    const matchSearch = text.includes(search);


    let matchFilter = true;

    if (activeFilters.size > 0) {

      matchFilter = [...activeFilters].every(f => tags.includes(f));

    }


    row.style.display = (matchSearch && matchFilter) ? "" : "none";

  });

}

</script>



<style>

  .ff-container {

    font-family:'IBM Plex Mono', monospace;

    margin:20px 0;

    color:#ffffff;

  }


  /* PANEL */

  .ff-panel {

    background:linear-gradient(145deg,dodgerblue,dodgerblue);

    border-radius:14px;

    padding:14px;

    box-shadow:0 0 20px rgba(30,144,255,0.6);

  }


  /* SEARCH */

  .ff-search {

    width:100%;

    padding:10px 14px;

    border-radius:8px;

    border:1px solid #ffffff33;

    background:dodgerblue;

    color:#ffffff;

    margin-bottom:10px;

  }


  /* FILTER CHIPS */

  .ff-chips {

    display:flex;

    flex-wrap:wrap;

    gap:8px;

    margin-bottom:12px;

  }


  .chip {

    padding:6px 10px;

    border-radius:999px;

    font-size:12px;

    background:dodgerblue;

    border:1px solid #ffffff33;

    cursor:pointer;

    transition:0.25s;

    color:#fff;

  }


  .chip.active {

    background:#ffffff;

    color:dodgerblue;

    box-shadow:0 0 10px #ffffff;

  }


  .chip:hover {

    box-shadow:0 0 8px #ffffff;

  }


  /* TABLE */

  .ff-table {

    width:100%;

    border-collapse:separate;

    border-spacing:0;

    background:dodgerblue;

    border-radius:12px;

    overflow:hidden;

  }


  .ff-table th {

    padding:14px;

    text-align:left;

    background:dodgerblue;

    border-bottom:1px solid #ffffff33;

    cursor:pointer;

    position:relative;

    color:#ffffff;

  }


  .ff-table th .arrow {

    position:absolute;

    right:10px;

    opacity:0.7;

  }


  .ff-table td {

    padding:12px;

    border-bottom:1px solid #ffffff33;

    transition:0.25s;

    position:relative;

    color:#ffffff;

  }


  /* ROW COLORS (slight variation for depth) */

  .ff-table tr:nth-child(even) td { background:#1c86ee; }

  .ff-table tr:nth-child(odd) td { background:dodgerblue; }


  /* ROW HOVER */

  .ff-table tr:hover td {

    background:#187bcd;

    box-shadow: inset 0 0 10px rgba(255,255,255,0.3),

                0 0 12px rgba(255,255,255,0.4);

  }


  /* COLUMN HIGHLIGHT */

  .ff-table td:hover::after,

  .ff-table th:hover::after {

    content:"";

    position:absolute;

    top:-5000px;

    bottom:-5000px;

    left:0;

    right:0;

    background:rgba(255,255,255,0.15);

    z-index:-1;

  }

</style>

Comments