Is it feasible to hide the key element until modified by my code using waitForKeyElements?

I've developed this userscript (with valuable assistance from Stack Overflow) specifically for the website metal-archives.com.

Here is the basic structure of the script:

function appendColumn(...) {
    // code for adding a column
    // code for enabling table sorting   
}

waitForKeyElements("table", appendColumn);        

The script functions well, except for a slight visual glitch/delay when switching between sub-tabs (tables).

Upon switching tabs, the additional (6th) column initially appears correctly. However, the table briefly reverts to its original form, before finally displaying with the 6th column as intended.

To observe this issue, install the script, visit this specific page, and switch between the sub-tabs (such as Complete Discography, Main, Lives, Demos, Misc, etc.).
Here's how it looks:

https://i.sstatic.net/ABMts.gif


I attempted to prevent the initial table from being displayed by adding the following to the beginning of appendColumn():

GM_addStyle(".display.discog {display: none;} ");

and then adding:

GM_addStyle(".display.discog {display: inline !important;} "); 

to the end of appendColumn().
However, this did not resolve the issue.


Using the Firefox Network Monitor on the page, it appears that when switching tabs:

  • The code immediately alters the table (possibly from cache, as there is no entry in the Network Monitor).
  • Subsequently, the table (HTML file) is fetched from the server.
  • Finally, the table is modified once more.

How can I adjust the code (while utilizing waitForKeyElements) to prevent the key element from being displayed initially, and only show it after it has been modified by my code?

Alternatively, how can I improve the speed of the response?

Your assistance is greatly appreciated.

Answer №1

After loading your script, I made some adjustments, added timing lines, and conducted some testing. The results showed that the time elapsed from completing the AJAX call until the table was fixed and fully functional ranged from 400 to 500 milliseconds. This speed should be sufficient for most users and scenarios.

However, if there are instances where every millisecond counts, you have the option to switch to using MutationObservers. While they are faster, they are also more delicate, less universal across browsers, and can be tricky to work with. In this particular scenario, implementing MutationObservers reduced the time from AJAX completion to the fixed table to a range of 20 to 40 milliseconds.

I would recommend utilizing a library like Mutation Summary to simplify the process and minimize any potential difficulties.

To transition from a basic waitForKeyElements() setup to Mutation Summary, follow these steps:

  1. Include

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

    in your metadata block.

  2. Integrate your waitForKeyElements callback and simple selector into the following 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, you would 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]) );
    }
}

Make sure to determine the rootNode by analyzing the structure of the page.




Below is a full script featuring 3 optional methods and timing logs. This script was tested on Firefox only, but it should be compatible with Tampermonkey as well.

Review the //OPTION n lines preceding each section to comment out specific parts if needed.

// ==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];     // reference to the table

    // To avoid further execution, stop if the current sub-table is empty
    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;    // Get the content of the current cell in the Review column and store it in 'k'


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

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

        if (re1.test(k) != 0){                    // Create new cells only if there are matches in the RegEx

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

            newCell.innerHTML = url[1] + l[1] + '</url>'; // Add the Ratings percentage as a link to Reviews


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

            newCell = tbl.rows[i].cells[cols];
            newCell.innerHTML = url[1] + m[1] + '</url>'; // Add the Reviews number as a link to 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 certain styling code and original comments have been excluded from this demonstration.

Answer №2

Don't rely on waitForKeyElements for inserting your content. The slow nature of this method is likely causing the rendering issues you are experiencing.

The tab appears correctly when initially switched because it is hidden and adjusted properly while hidden. However, when shown, the content is not updated to reflect the latest data from the server. This triggers waitForKeyElements to correct the display.

To hide the panel when the content is first loaded, utilize ajaxComplete like so:

$(document).ajaxComplete(function(e, xhr, config){
   if(config.url.indexOf('/tab/') != -1){
       $('.ui-tabs-panel').css('visibility', 'hidden');
   }
});

Once the content is loaded, make the panel visible in your appendColumn function:

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

Keep coding and stay happy!

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Is there a way to prevent text from overlapping a Material UI React Textfield when it is scrolled up and set to position sticky?

Scenario: In my chat application, I have the chat displayed in the middle of the screen with a sticky textfield at the bottom. My goal is to ensure that when users scroll through the chat messages, the textfield remains at the bottom but appears on top of ...

What is the correct way to handle fetch timeouts in a React component?

Utilizing a JavaScript timeout, I am able to fetch Dogs from my API successfully. However, there are instances where the timeout fails to clear properly: import { useState, useEffect, useCallback } from 'react'; const DogsPage = () => { c ...

Utilizing Onchange() to Implement AJAX Functionality

I'm attempting to utilize ajax to display the onchange value of a textbox. Here's the code I have so far: ajaxquery.php <html> <head> <title>Changing textbox value based on dropdown list using Ajax and PHP</title> <me ...

Exploring the shine: implementing reflection in THREE.js

Is there a way to achieve a material that reflects other shapes from the scene? I attempted to use the reflectivity property but had no success in seeing any reflection. I found an example demonstrating this effect It seems like non-standard materials we ...

What is the best way to access and verify data from an array that has been passed through props

I am working with a component that takes an array as a prop <Component runners={['1','2']}/> Inside this functional component, my goal is to generate an element for each value in the array. Here's my logic: const { runners ...

The browser window's location is redirected to an empty page

I'm currently working on a website that has a similar layout to this (). https://i.sstatic.net/c7I2y.png My main issue is with the code I've written to detect a click on the linkedin div and redirect accordingly. <script src="https://ajax.g ...

Using regular expressions in Sublime Text 2 to replace text in JSON files can greatly improve your workflow

After using an online web tool to convert an Excel file to JSON, I now require assistance in replacing certain values within the JSON. Currently, I am using Sublime Text 2. {"Time (GMT-04:00)":"2010-07-06 08:30:00","Skin temp - average":"34,2043","Step Co ...

Can you explain the functionality of the Json onLoad method and how can I use it to send a response?

function MakeJsonRequest() { JsonRequest.setRequestHeader("Content-type", "application/json"); JsonRequest.send(Data); JsonRequest.onload = function() { ProcessJsonResponse(this.responseText); } } // The Som ...

What causes the error message 'avoid pushing route with duplicate key' when using NavigationStateUtils in React Native?

Within my React Native + Redux project, I have set up a reducer for navigation utilizing NavigationStateUtils: import { PUSH_ROUTE, POP_ROUTE } from '../Constants/ActionTypes' import { NavigationExperimental } from 'react-native' impo ...

Tips for passing input fields from a React Function component to a function

How can I retrieve the values from 4 input fields in my react functional component using a function called contactMeButtonPressed? The inputs include name, email, company name, and message when the contact me button is clicked. Do I need to implement stat ...

Content is not centered with Bootstrap's flexbox

The alignment of content is not centered using bootstrap flex <div class="d-flex flex-row bd-highlight mb-3"> <div class="p-2 bd-highlight text-left"><i class="fa fa-chevron-left" aria-hidden="true"></i></div> <div cla ...

Using Ajax post to retrieve data from multiple tables in PHP and MYSQL

I'm attempting to achieve the following: Retrieve TMSGUID from the Campuses table using the ID (primary key). Retrieve TMSGUID from the Sites table. Retrieve ID and Description from the SiteOrganisation table by using Site GUID. The PHP page is bei ...

Why isn't my List<string> being retrieved from the MVC Controller in an $ajax request?

I am attempting to generate customized lists in my cshtml file through an ajax request. .ajax({ type: "GET", cache: false, url: "@Url.Action("getValidationLists")", contentType: "application/json", dataType: "json", ...

Designing an immersive full-screen experience with scrollable columns using Bootstrap

Trying to achieve a full-screen layout that occupies 100% of the viewport with a sticky header and footer, along with columns in the main content area that can be individually scrolled. I've been experimenting with using classes like .h-100 and .flex ...

Swap out the image for a div element if the image is not found

Is there a way to accurately display an image if it's available, and use a div as a replacement if the image is not present? // How can I determine `imageExists` without encountering cross-origin issues? (imageExists) ? (<img class="avatar-img" sr ...

What is the process of transferring parameters from JQuery to an ASP.NET webmethod?

When using this jQuery ajax method to call a webmethod, everything works smoothly except for the parameter passed as a User object, which ends up having empty fields. The values can be seen in Firebug during debugging, but they simply don't transfer t ...

Issue with Karma and angular-mocks: The error message "TypeError: 'undefined' is not an object (evaluating 'angular.mock = {}')" is being shown

I am facing an issue while writing unit tests using Karma + Jasmine. The problem arises with angular-mocks when I run grunt test, resulting in the following error message: PhantomJS 1.9.8 (Mac OS X) ERROR TypeError: 'undefined' is not an ob ...

Retrieve JSON information using PHP and JavaScript SDK exclusivley

After successfully setting up the Facebook Javascript SDK and getting it to display the right information on the page, I have also managed to echo the user into the Firebug console to show the Object containing all of the user profile details. * I am opti ...

Is it possible that images appear normal locally, but become stretched once deployed? (CSS/HTML only)

I designed a straightforward webpage using only HTML and CSS. Everything looks great when I view it on my local machine. However, once I deployed the site on Droppages, a static-page hosting service integrated with Dropbox, all my images became stretched ...

Setting the height of a Bootstrap radio button

My Bootstrap radio buttons are set to a width of 200px. However, when the text inside the button exceeds this width, it wraps onto two lines leading to changes in button height. How can I ensure that all other buttons displayed scale to the same height? h ...