0

I sped up my server response times and suddenly had a strange issue: I ran into a problem where code was being executed before the browser could draw certain elements that need to be on screen first. Instead of the problem being that the code needed to be in $.ajax().done(), I need essentially a done for the insertion of elements into the DOM.

The best option I've found so far is this answer to a similar question last year that uses async/await:

const checkElement = async selector => {
  while ( document.querySelector(selector) === null) {
    await new Promise( resolve =>  requestAnimationFrame(resolve) )
  }
  return document.querySelector(selector); 
};

Which I can invoke in my own code then like this:

   checkElement('#showFileBrowserContent').then( function (selector) {
        loadDirectory (path, "", nextMode, articleId, siteId, currentSetting, fileBrowserPanel, callback, mode);
    });

This seems to work just fine, but I was wondering if there's a way to make it work within a jQuery function just for neatness's sake, e.g.:

$('#showFileBrowserContent').checkElement().done( function () { loadDirectory (path, "", nextMode, articleId, siteId, currentSetting, fileBrowserPanel, callback, mode); });

I've never tried extending jQuery before, so I probably did this entirely incorrectly, but here is what I tried to make that happen:

jQuery.fn.checkElement = async function () {
    var o = $(this[0]) // This is the element
    
    // Also tried o === null without the $().
    while ( $( o ) === null) {
        await new Promise( resolve =>  requestAnimationFrame(resolve) )
    }
    
    return this; 
};

This did not work. Is it a problem with using async/await within a jQuery function? Or is it a problem with my code?


Update: Here's the code in question that I'm trying to make sure is finished inserting into the DOM before I move on. It uses BootstrapDialog:

    fileBrowserPanel = new BootstrapDialog({            
        title: title,
        buttons: buttons,
        cssClass: 'fileBrowser',
        onhide: function(mode){ 
                if (editorPanel !== null) { 
                    editorPanel.open(); editorPanel.setAutodestroy(true); 
                }   
            }
        });

    var editorContents = '<div class="row"><div id="fileBrowserCurrentLocation" class="col-xs-8"></div><div id="fileBrowserToolbar" class="col-xs-4"><p><button type="button" class="btn btn-default" aria-label="Upload a File" id="launchFileUpload"><span class="glyphicon glyphicon-cloud-upload" aria-hidden="true"></span> Upload a File</button></p></div></div>';
    editorContents += '<div class="row"><div id="showFileBrowserContent" class="col-xs-12"><table id="loaderTable">' + loaderGraphic + '</table></div></div>';


    fileBrowserPanel.setMessage(editorContents);
    fileBrowserPanel.setType(BootstrapDialog.TYPE_INFO);
    var response = fileBrowserPanel.open();         

That code has a spinner graphic inserted where the actual document data should go in #showFileBrowserContent. It then calls loadDirectory(), which has the AJAX call to get the actual content and replaces #showFileBrowserContent's contents with what is returned. However, without either a timer or the await function, it appears the AJAX call completes and #showFileBrowserContent doesn't yet exist. This wasn't a problem when my server response time was closer to 600ms, but when I knocked it down to closer to 200-300ms, the spinner would never go away and I noticed it appeared that was because the AJAX call completed before #showFileBrowserContent existed.

Timothy R. Butler
  • 1,097
  • 7
  • 20
  • If the elements are being added in the AJAX `.done()` function, anything that depends on them should also be called from that function. Updating the DOM is synchronous. – Barmar Apr 12 '21 at 17:00
  • Another option is to use a `MutationObserver` to detect when the element is added. – Barmar Apr 12 '21 at 17:02
  • That's what I thought (e.g. it was synchronous), which confuses me. The elements are being added using BootstrapDialog without AJAX. However, the next AJAX call completes and is unable to insert material into the dialog because it appears the AJAX call completes before BootstrapDialog draws the box. I set all the parameters of the dialog and then call `fileBrowserPanel.open();` right before the code in question. On the other hand, if I set a timer before executing the AJAX (or use the code I mentioned above), everything works. – Timothy R. Butler Apr 12 '21 at 17:09
  • Would `MutationObserver` be better/more efficient than the code above? The code works OK, it just seemed a bit less elegant than what I'd imagine a jQuery alternative would be. – Timothy R. Butler Apr 12 '21 at 17:10
  • Added more details above about what I'm wrestling with so it is a little clearer. – Timothy R. Butler Apr 12 '21 at 17:52

0 Answers0