0

I'm loading a form and accompanying JS scripts into a modal via .ajax, but the JS isn't firing or initialising the elements as expected. (Nothing happens) The HTML and scripts work as expected elsewhere except when loaded via ajax into a modal.

Here's how I'm loading the content using a Promise to make sure and load the scripts only after the HTML is loaded. Any pointers where I may be going wrong?

$(document).on('click', '#post-offer', function() {
        $.when(
              $('head').append('<link rel="stylesheet" href="' + baseURL + '/css/jquery-ui.css">'),
            $('head').append('<link rel="stylesheet" href="' + baseURL + '/css/jquery.tagit.css">'),
            $('head').append('<link rel="stylesheet" href="' + baseURL + '/slim/slim.min.css">'),
            $.getScript( "//ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js"),
            openModal(baseURL + '/ajax.cgi?a=showoffer'), // << THIS IS THE HTML FORM
            $.Deferred(function(deferred){
                $(deferred.resolve);
            })
        ).done(function(){
            $.getScript( "//maps.googleapis.com/maps/api/js?v=3&key=xxxxxxxxxxxxxx"),
            $.getScript( baseURL + "/js/tag-it.js" ),
            $.getScript( baseURL + "/slim/slim.jquery.js" ),
            $.getScript( baseURL + "/js/listing.js" );
            
        });
});

For completeness, here's the openModal function (which works as expected):

function openModal(arg) {
    $('#loading').show();
    if (arg.match(/^http/)) { //If query, then send it.
        var $query = arg;
        $.ajax({
            url: $query,
            success: function(result) {
                $('#modalWrap').show();
                $('#modalContent').html(result);
                $('#loading').hide();
            },
            error: function(xhr) {
                $("#loading").hide();
                alert('Communication error! [Details: ' + xhr.status + ' - ' + xhr.statusText + ']');
            }
        });
    } else { //Or just return the input argument
        $('#modalWrap').show();
        $('#modalContent').html(arg);
        $('#loading').hide();
    };
};
Colin R. Turner
  • 1,323
  • 15
  • 24
  • is openModal defined by you? or is that some kind of jquery extension? – Joey Carlisle Feb 22 '22 at 19:19
  • It's my JS function that opens the modal and loads the HTML. It works as expected, but when loaded with scripts, the loaded scripts apparently don't 'see' the HTML. – Colin R. Turner Feb 22 '22 at 19:24
  • Added the openModal function to question – Colin R. Turner Feb 22 '22 at 19:27
  • 1
    my suggestion would be to avoid loading the scripts via html injection. I would load/lazy load the scripts before hand. Then your click-handler merely has to call openModal – Joey Carlisle Feb 22 '22 at 19:42
  • Thanks I would like to avoid pre-loading scripts if possible since it's likely only a small percent of users who will execute the posting form – Colin R. Turner Feb 22 '22 at 19:45
  • 1
    here is lazy loading docs, which would at least not slow down the initial load: https://developer.mozilla.org/en-US/docs/Web/Performance/Lazy_loading#javascript – Joey Carlisle Feb 22 '22 at 19:49
  • another resource: https://stackoverflow.com/questions/14521108/dynamically-load-js-inside-js – Joey Carlisle Feb 22 '22 at 19:51
  • If you put `console.log('Context Count: ' + $(#post-offer).length)` just before your `$(document).on('click', '#post-offer', function() {` line what do you see? – Lajos Arpad Mar 03 '22 at 15:59
  • @ColinR.Turner do you see any errors in console logs? – the Hutt Mar 06 '22 at 16:19
  • @theHutt no errors in console. – Colin R. Turner Mar 07 '22 at 16:01
  • 1
    @LajosArpad: ``Context Count: 1`` – Colin R. Turner Mar 07 '22 at 16:06
  • shouldn't `openModal` function return a promise you are waiting for all the arguments inside `when` to be executed then load the js files. you could create a promise that is resolved after ajax call inside openModal function – Arshia Moghaddam Mar 08 '22 at 11:39
  • @ColinR.Turner interesting. This means that the item already exists at that point. What if you put `console.log('I am here')` into your function defined to be executed upon that click as the first operation? Does it display `I am here` upon doing a click? – Lajos Arpad Mar 08 '22 at 13:20
  • @LajosArpad, yes code inside the function executes no problem, but nothing happens. I'm guessing it's a binding issue rather than an execution issue? – Colin R. Turner Mar 08 '22 at 15:25
  • What's odd is that there are no console errors, which leads me to suspect that maybe AJAX loaded scripts can't report errors to console? – Colin R. Turner Mar 08 '22 at 15:27
  • You load 3 CSS files and a JS file. Are they successfully loaded? – Lajos Arpad Mar 09 '22 at 10:37
  • I solved it. The problem was with `listing.js` which was responsible for instantiating JS elements on the page. It was contained in a `('document').ready()` which wasn't firing since DOM was already loaded. Instead I placed the contents of `listing.js` inside a function, loaded the HTML last and called that function in the dynamically loaded HTML. Works perfectly now. Thanks all. – Colin R. Turner Mar 09 '22 at 14:51

2 Answers2

4

try replacing this with your openModal function:

function openModal(arg) {
  return new Promise((resolve, reject) => {
    $("#loading").show();
    if (arg.match(/^http/)) {
      //If query, then send it.
      var $query = arg;
      $.ajax({
        url: $query,
        success: function (result) {
          $("#modalWrap").show();
          $("#modalContent").html(result);
          $("#loading").hide();
          resolve();
        },
        error: function (xhr) {
          $("#loading").hide();
          alert(
            "Communication error! [Details: " +
              xhr.status +
              " - " +
              xhr.statusText +
              "]"
          );
          reject()
        },
      });
    } else {
      //Or just return the input argument
      $("#modalWrap").show();
      $("#modalContent").html(arg);
      $("#loading").hide();
      resolve();
    }
  });
}

if that alone didn't help, replace done with this after you added the code above:

done(function () {
  window.requestAnimationFrame(() => {
    $.getScript("//maps.googleapis.com/maps/api/js?v=3&key=xxxxxxxxxxxxxx");
    $.getScript(baseURL + "/js/tag-it.js");
    $.getScript(baseURL + "/slim/slim.jquery.js");
    $.getScript(baseURL + "/js/listing.js");
  });
});
Arshia Moghaddam
  • 440
  • 6
  • 19
  • Thanks. I solved it. It wasn't a loading problem but a problem with getting the functions once everything was loaded. In the end, I wrapped the important JS in a function and called that function from the loaded HTML. Works fine now. – Colin R. Turner Mar 09 '22 at 14:55
0

Here's how I solved it for anyone else with this problem.

It wasn't a loading problem. All the scripts and HTML loaded fine. The problem was that one of the dynamically loaded scripts listing.js had a document.ready() wrapper which wasn't firing since DOM was already loaded, so JS elements on the dynamically-loaded form weren't being initialized. However, when I removed the document.ready(), it still wasn't initializing the dynamically-loaded elements.

To get around this, I changed the document.ready() to a function initialize() and called that last from the dynamically-loaded HTML. It all works fine now:

//DYNAMICALLY LOADED SCRIPT FIRST    
function initialize(){

    //INITIALIZE JS FORM ELEMENTS...

    }

Then, in the same AJAX call:

<form class="dynamically-loaded-HTML">

<!-- FORM ELEMENTS -->

</form>
<script>
initialize();
</script>
Colin R. Turner
  • 1,323
  • 15
  • 24