Skip to content Skip to sidebar Skip to footer

Using WaitForKeyElements, Is It Possible To Prevent The Key Element From Being Displayed, And Only Display It After It's Modified By My Code?

I have this userscript (written with a great help from Stack Overflow) for the site metal-archives.com. Its structure is like this: function appendColumn(...) { // code for a

Solution 1:

I loaded your script, added timing lines, and tested it. The time elapsed from AJAX-complete until the table was fixed and finished was only 400 to 500 milliseconds! That's plenty fast for most people and situations.

But, for those times when you absolutely want to squeeze out the milliseconds, you can switch to MutationObservers. These are finicky, brittle, and less cross-browser portable, but they are fast.
In this case, MutationObservers took the AJAX-to-fixed-table time down to the 20 to 40 millisecond range.

I recommend using a library like Mutation Summary to take some of the pain out of the process.

To convert from a simple waitForKeyElements() implementation to Mutation Summary:

  1. Add

    @require https://raw.githubusercontent.com/rafaelw/mutation-summary/master/src/mutation-summary.js
    

    to your metadata block.

  2. Plug your waitForKeyElements callback and simple selector into this structure:

    var muteObserver = new MutationSummary ( {
        callback: handleDiscographyChanges,
        rootNode: $( {ANY-JQUERY-SELECTOR} )[0],
        queries: [ {element: {A-SIMPLE-SELECTOR}} ]
    } );
    
    function handleDiscographyChanges (muteSummaries) {
        var mSummary    = muteSummaries[0];
        if (mSummary.added.length) {
            {YOUR-CALLBACK} ( $(mSummary.added[0]) );
        }
    }
    

For example in this case, change:

waitForKeyElements (".display.discog", appendColumn);

To:

var muteObserver = new MutationSummary ( {
    callback: handleDiscographyChanges,
    rootNode: $("#band_disco")[0],
    queries: [ {element: ".discog"} ]
} );

function handleDiscographyChanges (muteSummaries) {
    var mSummary    = muteSummaries[0];
    if (mSummary.added.length) {
        appendColumn ( $(mSummary.added[0]) );
    }
}

Where the rootNode was determined by examining the page structure.




For reference, a complete script with 3 optional approaches and timing logging is below. It was only tested on Firefox, but should work with Tampermonkey too (maybe).

See the //OPTION n lines just above each line to optionally comment out.

// ==UserScript==
// @name        Metal Archives (discography pages) - Reviews column split and sortable tables
// @include     http://www.metal-archives.com/bands/*
// @include     http://www.metal-archives.com/band/*
// @grant       none
// @require     http://code.jquery.com/ui/1.9.1/jquery-ui.min.js
// @require     https://greasyfork.org/scripts/2199-waitforkeyelements/code/waitForKeyElements.js?version=6349
// @require     https://greasyfork.org/scripts/5844-tablesorter/code/TableSorter.js?version=21758
// @require     https://raw.githubusercontent.com/rafaelw/mutation-summary/master/src/mutation-summary.js
// ==/UserScript==

function appendColumn(jNode) {
    logTime ("Table fixed");

    // STEP 1+2: SPLIT THE 'REVIEWS' COLUMN INTO A 'REVIEWS' COLUMN AND A 'RATINGS' COLUMN
    var tbl = jNode[0];     // table reference

    // If the current sub-table has no data, then stop the execution of the function
    if (tbl.rows[1].cells[0].innerHTML == '<em>Nothing entered yet. Please add the releases, if applicable. </em>') {
        return;
    }

    var newCell, newText;

    const cols = tbl.rows[0].cells.length - 1;

    var tr = tbl.tHead.children[0],
    th = document.createElement('th');

    th.innerHTML = "Ratings";
    th.className = "ratingsCol";
    tr.appendChild(th);

    for (i = 1; i < tbl.rows.length; i++) {
        k = tbl.rows[i].cells[cols].innerHTML;    // Retrieve the content of the current cell of the Review column and store it to variable k


        re1 = /<a [^>]*>[^(]*[(]([^)]+)/ ;        // (RegEx which matches the 'Ratings' percentage(incl.the % symbol)
        l = re1.exec(k);                          // (Execute the RegEx and store it to variable l)

        newCell = tbl.rows[i].insertCell(-1);     // Add a new cell (for the new 'Ratings' column ) -for each row-

        if (re1.test(k) != 0){                    // If the RegEx has matches, (only) then create new cells with...

            re0 = /(<a [^>]*>)[0-9]*[^(]/ ;       // (RegEx which matches the reviews URL)
            url = re0.exec(k);                    // (Execute the RegEx and store it to variable url)

            newCell.innerHTML = url[1] + l[1] + '</url>'; // ...the Ratings percentage (which is also a link to the Reviews)...


            re2 = /<a [^>]*>([0-9]*)[^(]/ ;       // (RegEx which matches the 'Reviews' number)
            m = re2.exec(k);                      // (Execute the RegEx and store it to variable m)

            newCell = tbl.rows[i].cells[cols];    //
            newCell.innerHTML = url[1] + m[1] + '</url>'; // ...and the Reviews number (which is also a link to the Reviews)
        }
    }

    //  STEP 3: MAKE THE DISCOGRAPHY TABLE SORTABLE  (using the jQuery plugin "tablesorter")
    $(tbl).tablesorter ( {
        cssAsc: 'up',
        cssDesc: 'down',
        headers: {
              0: {sorter: false}
        }
    } );
}

//OPTION 1
//waitForKeyElements (".display.discog", appendColumn);

$(document).ajaxComplete (function (e, xhr, config){
    logTime ("Ajax complete");
    //OPTION 2
    return; //-- For compare test

    if (config.url.indexOf ('/tab/') != -1){
        $(".display.discog").each ( function () {
            appendColumn ( $(this) );
        } );
    }
} );

$("#band_disco > ul > li").on ("click", "a.ui-tabs-anchor", function (zEvent) {
    logTime (zEvent.target.textContent + " tab was clicked.");
} );

function logTime (lableTxt) {
    var tNow    = new Date ();
    console.log (tNow.toLocaleFormat ('%H:%M:%S') + "." + tNow.getMilliseconds (), " <== " + lableTxt);
}

//OPTION 3
//*--- Remove leading slash, from this line, to comment out block, below.
var muteObserver = new MutationSummary ( {
    callback: handleDiscographyChanges,
    rootNode: $("#band_disco")[0],
    queries: [ {element: ".discog"} ]
} );
//*/ -- Tail end of optional comment block

function handleDiscographyChanges (muteSummaries) {
    var mSummary    = muteSummaries[0];
    if (mSummary.added.length) {
        appendColumn ( $(mSummary.added[0]) );
    }
}

Note that styling code, and some original comments, were omitted from this example.


Solution 2:

waitForKeyElements is a slow way to insert your content. This is one reason you're seeing the rendering behavior that you are seeing.

The reason the tab is rendered appropriately when you first switch is because it is hidden and correctly adjusted while hidden. When the tab is shown the content is updated to reflect the latest from the server, which has not been adjusted. waitForKeyElements then notices the change and fires again to correct.

Using ajaxComplete you can hide the panel when the content is first loaded.

Hiding the panel when data first returns:

$(document).ajaxComplete(function(e, xhr, config){
   if(config.url.indexOf('/tab/') != -1){                     // Because the tables URLs are like `www.metal-archives.com/band/discography/id/xxx/tab/lives`
       $('.ui-tabs-panel').css('visibility', 'hidden');
   }
});

Showing the panel in your appendColumn function:

function appendColumn(...) {
    // code for appending column
    // code for making the table sortable   
    $('.ui-tabs-panel').css('visibility', 'visible');
}

Happy coding!


Post a Comment for "Using WaitForKeyElements, Is It Possible To Prevent The Key Element From Being Displayed, And Only Display It After It's Modified By My Code?"