Search code examples
javascripttwitter-bootstrapdatatables

javascript to show only part of elements with marked in one cell in datatable


Live html/js code https://jsfiddle.net/larrycai/9e8rg76f/2/ (data is sample, not real)

enter image description here

I have lots of 3pps for scanned container images, see column 3pp lists, normally it could have 100+ 3pps.

In order to have better UX, I want

  • it defaults to show 20 components (with ...) in end inside the cell
  • if something is searched in the search box, those filtered 3pps can be marked out (datatables.mark.js? in the cell
    • if too many are marked, then show 20 components as maximum still
  • it will be good to have more/less button as well, so if user want to expand the row, all the 3pps can be expanded.

I tried to use render() function, bit it seems if I filtered, then Datatable search can't cover all original 3pp list

   var table = $('#dataTable').DataTable({
     "data": jsonData,
     "columns": [{
         "data": "name"
       },
       {
         "data": "image",
         "render": function(data, type, row, meta) {
           return "" + data + "";
         }
       },
       {
         "data": "3pp",
         "render": function(data, type, row, meta) {
           /*
           1. if nothing in search (initial), show first SHOW_NUM keywords + ...
           2. if value in search, show matched 3pps, if it is bigger than 20, show first 20 keywords + ...
           */
           return data.slice(0, SHOW_NUM) + " ..."
         }

Solution

  • I've never understood why render replaces the data. Instead, use callback after the row has been rendered to amend the rendered cell using the data attribute. I've used rowCallback and jQuery data cell click callback, which passes the referenced row and data to a small prettify3pp function that quote renders unquote the cell with updated content.

    Add a little few bootstrap badges and voila.

    var jsonData = [{
      "name": "ubuntu",
      "image": "ubuntu:focal",
      "3pp": ['acl:2.3.1-1', 'adduser:3.118ubuntu5', 'apt:2.4.8', 'attr:2.5.1-1build1', 'audit:3.0.7-1build1', 'base-files:12ubuntu4.2', 'base-passwd:3.5.52build1', 'bash:5.1-6ubuntu1', 'berkeleydb:5.3.28+dfsg1-0.8ubuntu3', 'bzip2:1.0.8-5build1', 'cdebconf:0.261ubuntu1', 'coreutils:8.32-4.1ubuntu1', 'dash:0.5.11+git20210903+057cd650a4ed-3build1', 'debconf:1.5.79ubuntu1', 'debianutils:5.5-1ubuntu2', 'diffutils:3.8-0ubuntu2', 'dpkg:1.21.1ubuntu2.1', 'e2fsprogs:1.46.5-2ubuntu1.1', 'findutils:4.8.0-1ubuntu3', 'gcc-12:12.1.0-2ubuntu1~22.04', 'glibc:2.35-0ubuntu3.1', 'gmp:6.2.1+dfsg-3ubuntu1', 'gnupg:2.2.27-3ubuntu2.1', 'gnutls:3.7.3-4ubuntu1.1', 'grep:3.7-1build1', 'gzip:1.10-4ubuntu4.1', 'hostname:3.23ubuntu2', 'iconv:2.35', 'init-system-helpers:1.62', 'kerberos:', 'keyutils:1.6.1-2ubuntu3', 'krb5:1.19.2-2', 'libcap:', 'libcap:2.44-1build3', 'libcap-ng:0.7.9-2.2build3', 'libffi:3.4.2-4', 'libgcrypt:1.9.4-3ubuntu3', 'libgpg-error:1.43-3', 'libidn2:2.3.2-2build1', 'libnsl:', 'libnsl:1.3.0-2build2', 'libseccomp:2.5.3-2ubuntu2', 'libsemanage:3.3-1build2', 'libsepol:3.3-1build1', 'libtasn1:4.18.0-4build1', 'libtasn1-6:4.18.0-4build1', 'libtirpc:1.3.2-2ubuntu0.1', 'libunistring:1.0-1', 'libxcrypt:4.4.27-1', 'linux-pam:1.4.0-11ubuntu2', 'lsb:11.1.0ubuntu4', 'lz4:1.9.3-2build2', 'mawk:1.3.4.20200120-3', 'ncurses:6.3-2', 'nettle:3.7.3-1build2', 'openssl:3.0.2-0ubuntu1.6', 'p11-kit:0.24.0-6build1', 'pcre:8.39-13ubuntu0.22.04.1', 'pcre2:10.39-3ubuntu0.1', 'perl:5.34.0-3ubuntu1.1', 'procps:3.3.17-6ubuntu2', 'readline:', 'sed:4.8-1ubuntu2', 'selinux:3.3-1build2', 'sensible-utils:0.0.17', 'shadow:4.8.1-2ubuntu2', 'systemd:249.11-0ubuntu3.6', 'sysvinit:3.01-1ubuntu1', 'tar:1.34+dfsg-1build3', 'tdb:1.46.5', 'ubuntu-keyring:2021.03.26', 'usrmerge:25ubuntu2', 'util-linux:2.37.2-4ubuntu3', 'xxhash:0.8.1-1', 'xz:5.2.5-2ubuntu1', 'zlib:1.2.11.dfsg-2ubuntu9.2', 'zstd:1.4.8+dfsg-3build1'],
      "report": "report-581-20221104-154434.pdf"
    }, {
      "name": "toolkit",
      "image": "ubuntu-tookit:jammy",
      "3pp": ['jfrog-jfrog-cli:', 'jfrog-jfrog-cli-core:v2.20.3', 'jfrog-jfrog-client-go:v1.21.0', 'jq:1.6-3.3.1', 'json-c:0.13-3.3.1', 'jszwec-csvutil:v1.7.1', 'jwalterweatherman:v1.1.0', 'kerberos:', 'kerberos:1.19', 'kevinburke-ssh_config:v0.0.0-20201106050909-4977a11b4351', 'keyutils:1.6.3-5.6.1', 'klauspost-compress:v1.13.5', 'kmod:29-4.15.1', 'less:530-3.3.2', 'libassuan:2.5.1-2.14', 'libatomic1:', 'libcap:', 'libcap:2.26-4.6.1', 'libcap-ng:0.7.9-4.37', 'libdevmapper:1.03.01', 'libedit:3.1.snap20150325-2.12', 'libfdisk:2.36.2', 'libffi:3.2.1.git259-10.8', 'libgcrypt:1.8.2-8.42.1', 'libgpg-error:1.29-1.8', 'libidn2:2.2.0-3.6.1', 'libksba:1.3.5-2.14', 'libmnl:1.0.4-1.25', 'libmpc3:1.1.0-1.47', 'libnsl:', 'libnsl:1.2.0-2.44', 'libproxy:0.4.15-12.41', 'libqrencode:4.0.0-1.17', 'libsanitizer:', 'libseccomp:', 'libsemanage:3.0-1.27', 'libsepol:3.0-1.31', 'libsolv:0.7.22', 'libssh:0.8.7-10.12.1', 'libtasn1:4.13-4.5.1', 'libtirpc:1.2.6-150300.3.3.1', 'libunistring:0.9.10-1.1', 'libusb:1.0.21-3.3.1', 'libxml2:2.9.7', 'linux-pam:', 'lua:5.3.6-3.6.1', 'lz4:1.9.2-3.3.1', 'make:4.2.1-7.3.2', 'manifoldco-promptui:v0.9.0', 'mergo:v0.3.12', 'minio-sha256-simd:v1.0.1-0.20210617151322-99e45fae3395', 'mitchellh-mapstructure:v1.5.0', 'mpfr:4.0.2-3.3.1', 'ncurses:6.1-5.9.1', 'nghttp2:1.40.0-6.1', 'npth:1.5-2.11', 'olekukonko-ts:v0.0.0-20171002115256-78ecb04241c0', 'oniguruma:6.7.0-1.19', 'openldap:', 'openslp:2.0.0-6.15.1', 'openssl:1.1.1d', 'owenrumney-go-sarif:v2.1.2', 'p11-kit:0.23.2-4.13.1', 'packaging:16.8', 'packaging:19.2', 'packaging:20.1', 'patch:2.7.6-3.5', 'pcre:8.45-20.10.1', 'pcre2:10.31-3.3.1', 'pep517:0.7.0', 'perl:5.26.1-150300.17.3.1', 'pgzip:v1.2.5', 'pierrec-lz4:v4.1.2', 'pinentry:1.1.0-4.3.1', 'pip:20.0.2', 'pkg-browser:v0.0.0-20210911075715-681adbf594b8', 'pkg-config:0.29.2-1.436', 'pkg-term:v1.1.0', 'popt:1.16-3.22', 'procps:3.3.15-7.22.1', 'properties:v1.8.6', 'protobuf:3.9.2-4.12.1', 'python:3.6.15-150300.10.21.1', 'rardecode:v1.1.0', 'readline:7.0', 'requests:2.22.0', 'rfkill:', 'rivo-uniseg:v0.2.0', 'rpm:4.14.3-150300.46.1', 'rsync:3.1.3-4.10.1', 'rubyist-tracerx:v0.0.0-20170927163412-787959303086', 'safestring:', 'sed:4.4-11.6', 'selinux:3.0-1.31', 'sergi-go-diff:v1.1.0', 'setuptools:44.1.1', 'shadow:', 'spew:v1.1.1', 'spf13-pflag:v1.0.3', 'spf13-pflag:v1.0.5', 'sqlite3:3.36.0-3.12.1', 'ssgelm-cookiejarparser:v1.0.1', 'sysfsutils:2.1.0-3.3.1', 'systemd:246.16-150300.7.45.1', 'tar:1.34-150000.3.12.1', 'testify:v1.5.1', 'testify:v1.8.0', 'ulikunitz-xz:v0.5.9', 'urfave-cli:v1.22.9', 'urllib3:1.25.7', 'util-linux:', 'util-linux:2.36.2', 'vbauerster-mpb:v7.4.2', 'vim:8.0.1568-5.17.1', 'viper:v1.12.0', 'visual_studio_runtime:', 'webencodings:0.5.1', 'wget:1.20.3-3.12.1', 'x-crypto:v0.0.0-20190426145343-a29dc8fdc734', 'x-crypto:v0.0.0-20220622213112-05595931fe9d', 'x-mod:v0.5.1', 'x-net:v0.0.0-20200301022130-244492dfa37a', 'x-net:v0.0.0-20220520000938-2e3eb7b945c2', 'x-sync:v0.0.0-20190911185100-cd5d95a43a6e', 'x-sys:v0.0.0-20200302150141-5c8b2ff67527', 'x-sys:v0.0.0-20220520151302-bc2c85ada10a', 'x-term:v0.0.0-20220526004731-065cf7ba2467', 'x-text:v0.3.0', 'x-text:v0.3.7', 'x-xerrors:v0.0.0-20220517211312-f3a8303e98df', 'xanzy-ssh-agent:v0.3.1', 'xi2-xz:v0.0.0-20171230120015-48954b6210f8', 'xo-terminfo:v0.0.0-20210125001918-ca9a967f8778', 'xxhash:', 'xz:5.2.3-150000.4.7.1', 'zlib:1.2.11-150000.3.30.1', 'zlib:1.2.3', 'zlib:1.2.5', 'zlib:1.2.8', 'zstd:1.4.4-1.6.1'],
      "report": "report-584-20221104-154434.pdf"
    }];
    var SHOW_NUM = 20
    $(document).ready(function() {
    
      const prettify3pp = (row, data, searchTerm, showAll) => {
        let deps
        if (searchTerm === '') {
          deps = data['3pp'].map(dep => `<span class="badge bg-secondary">${dep}</span>`)
        } else {
          deps = data['3pp'].filter(dep => dep.toLowerCase().includes(searchTerm)).map(dep => `<span class="badge bg-primary">${dep}</span>`)
        }
        if (deps.length > SHOW_NUM && !showAll) {
          const totalLength = deps.length
          deps = deps.slice(0, SHOW_NUM)
          deps.push(`<span class="badge bg-info show-all">Showing ${deps.length} of ${totalLength} - Show all</span>`)
        }
        row.querySelector('td.shorten').innerHTML = deps.join('<span hidden>,</span> ')
      }
      const table = $('#dataTable').DataTable({
        "data": jsonData,
        "columns": [{
            "data": "name"
          },
          {
            "data": "image",
            "render": function(data, type, row, meta) {
              return "<a href='https://hub.docker.com/_/" + data + "'>" + data + "</a>";
            }
          },
          {
            "data": "3pp",
            className: 'shorten',
          },
          {
            "data": "report",
            "render": function(data, type, row, meta) {
              return "<a href='" + data + "'>" + data + "</a>"
            }
          }
        ],
        "rowCallback": function(row, data) {
          const searchTerm = this.api().search().toLowerCase()
          //console.log('rowCallback', row, data, searchTerm)
          prettify3pp(row, data, searchTerm)
        }
      })
    
      $('#dataTable tbody').on('click', 'td.shorten .show-all', function() {
        const row = $(this).closest('tr')[0]
        const data = table.row(row).data()
        const searchTerm = table.search().toLowerCase()
        //console.log('clicked row', row, data, searchTerm)
        prettify3pp(row, data, searchTerm, true)
      });
    });
    .show-all {
      cursor: pointer;
    }
    <script src="https://code.jquery.com/jquery-3.5.1.js"></script>
    <script src="https://cdn.datatables.net/1.12.1/js/jquery.dataTables.min.js"></script>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/5.2.0/css/bootstrap.min.css">
    <link rel="stylesheet" href="https://cdn.datatables.net/1.12.1/css/dataTables.bootstrap5.min.css">
    
    <div class="container-fluid">
      <h2>Filter the 3pps: </h2>
      <table id="dataTable" class="datatables-table table table-striped table-hover table-bordered" style="width:100%">
        <thead>
          <tr>
            <th>Group</th>
            <th>container image</th>
            <th>3pp lists</th>
            <th>PDF</th>
          </tr>
        </thead>
        <tbody>
        </tbody>
      </table>
    </div>