Search code examples
javascripthtmlcsstabulatorwavesurfer.js

Looking to Combine Tabulator & Wavesurfer.js Waveform Pictures Into Indivudual Rows


i am trying to display a database of sound files and their accompanying waveform picture. this picture is an example of what i'm trying to accomplish:

example of what i'm trying to accomplish

i have been able to successfully work with and manipulate Tabulator and wavesurfer.js independently with ease, but am having issues when trying to combine the two.

var table = new Tabulator("#example-table", {
      height: "100%",

      layout: "fitDataFill",
      resizableColumns: false,
      data: cheeseData,
      columns: [{
        title: "Sounds Database",
        field: "type",
        sorter: "string"
      }, ],
      rowFormatter: function(row) {
        var element = row.getElement(),
          data = row.getData(),
          width = element.offsetWidth,
          rowTable, cellContents;

        //clear current row data
        while (element.firstChild) element.removeChild(element.firstChild);

        //define a table layout structure and set width of row
        rowTable = document.createElement("table")
       
        //rowTable.style.width = (width) + "px";
        rowTabletr = document.createElement("tr");

        //add image on left of row
        cellContents = "<td><img src='/images/" + data.image + "'></td>";

        //add row data on right hand side
        cellContents += "<td><div><strong>Type:</strong> " + data.type + "</div><div><strong>Length:</strong> " + data.length + "</div><div><strong>Title:</strong> " +
        data.title + "</div></td>";

        cellContents += "<td><div id='waveform2'></div></td>"



        rowTabletr.innerHTML = cellContents;



        rowTable.appendChild(rowTabletr);


        //append newly formatted contents to the row
        element.append(rowTable);





      },

      rowClick: function(e, row) {

        data = row.getData();

        wavesurfer.load('../audio/' + data.filename);
        wavesurfer.on('ready', function() {
          wavesurfer.play();
        });
        //e - the click event object
        //row - row component

      },


    });
    var wavesurfer2 = WaveSurfer.create({
      container: '#waveform2',
      scrollParent: true
    });
    wavesurfer2.load('../audio/' + data.filename);

While constructing each row, I am creating a new div entitled 'waveform2' that i would like to put the picture of that specific audio file next to it:

cellContents += "<td><div id='waveform2'></div></td>"

that seems to work fine, but when i try to draw the wavesurfer.js waveform (inside the row creation loop) into the div it doesn't work:

var wavesurfer2 = WaveSurfer.create({
      container: '#waveform2',
      scrollParent: true
    });
    wavesurfer2.load('../audio/' + data.filename);

placing this same code block outside of the loop (as indicated in the code block above) does draw a single waveform into the top div but not the other waveform2's that were created, not even duplicates.

i think i'm on the right track, or at least conceptually. how would i create an html div in each row that i can place a wavesurver.js waveform into?

i am but a mere coding bootcamp student looking for a nudge in the right direction. any insight you could provide to help me get over this hurdle would be great. thank you very much.


Solution

  • There are a few issues here.

    The first is that Tabulator uses a virtual DOM, it is therefor not possible to manipulate row contents successfully from outside the table, it must be done from inside formatters.

    The reason the wavesurfer pluggin is failing is because haven't been added to the DOM yet so the query selector you are passing to the wavesurfer plugging cant draw the wave. the elements are only added to the DOM after the rowFormatter function has returned.

    You need to place the wave surfer functions inside the rowFormatter and then inside a setTimeout to give the rowFormatter a chance to build the row before calling the plugin:

    //give the row time to render before calling the wavesurfer plugin
    setTimeout(() => {
        wavesurfer2 = WaveSurfer.create({
           container: '#waveform2',
          scrollParent: true
        });
        wavesurfer2.load('../audio/' + data.filename);
    }, 50); 
    

    The next issue is that you are trying to reference the container by id. the id tag on an element must be unique but every row you are creating contains an id of "waveform2" so only the first one will ever be show. it would be better to pass the element directly into the container property if it allows it:

    So what was:

    cellContents += "<td><div id='waveform2'></div></td>"
    rowTabletr.innerHTML = cellContents; 
    

    Should become:

    rowTabletr.innerHTML = cellContents; 
    
    var td = document.createElement("td");
    var waveformDiv = document.createElement("div");
    
    td.appendChild(waveformDiv);
    rowTabletr.appendChild(td);
    
    //give the row time to render before calling the wavesurfer plugin
    setTimeout(() => {
        wavesurfer2 = WaveSurfer.create({
           container: waveformDiv,  // <---- pass node for container div directly to plugin
          scrollParent: true
        });
        wavesurfer2.load('../audio/' + data.filename);
    }, 50);
    
    

    or if you cant do that then generate a unique id for the div for each row