2
$('body').append($('#add-done-modal, #add-failed-modal', data));

This code should append the div with id add-done-modal and add-failed-modal to body. Those div are served in data among other stuff. I'm sure the html in data is perfectly fine and intended. The code never find the id. I'm using jQuery 3.3.1. I know classes are a solution, but would be rather unwieldy due to various modal that could be or not chosen depending on the client calling.

EDIT: A simple $('#add-done-modal', data) returns an empty jQuery object. I am, once again, sure #add-done-modal is in data.

EDIT: This function is called by a clickevent:

function popup(url, name) {
  $.get(url, (data) => {
    $('#loading-modal .modal-body').html($('#product', data));
    // Can't find '#add-done-modal' nor '#add-failed-modal'
    let x = $('#add-done-modal', data);
    x = $('#add-failed-modal', data);
    $('body').append($('#add-done-modal, #add-failed-modal', data));
    // Initialize jQuery-UI or Bootstrap stuff loaded above here. Working.
  });

  $('#loading-modal .modal-header .modal-title').html(name);
  $('#loading-modal').modal('show');
}

Here is the content returned by the AJAX call:

<!DOCTYPE html>
<html lang="fr">
  <head>
    <!-- Head stuff -->
  </head>
  <body>
    <div id="all">
      <div class="top-bar">
        <div class="container">
          <div class="row d-flex align-items-center">
            <div class="col-8 col-lg-9">
              <form action="/updatelocale/" method="POST">
                <select class="bs-select" onchange="this.form.submit();">
                  <!-- Locale options, can be disregarded -->
                </select>
              </form>
            </div>
            <div class="col justify-content-md-end">
              <div class="row">
                <!-- Login and some buttons, not used -->
              </div>
            </div>
          </div>
        </div>
      </div>
      <header class="nav-holder make-sticky">
        <div id="navbar" role="navigation" class="navbar navbar-expand-lg">
          <!-- A pretty navbar -->
        </div>
      </header>
      <div id="heading-breadcrumbs">
        <!-- Cute breadcrumbs -->
      </div>
      <div id="content">
        <!-- Product, loading this from data works -->
        <div id="product" class="container">
          <div id="productMain" class="row">
            <!-- Stuff inside #product -->
          </div>
        </div>
      </div>
      <footer class="main-footer">
        <!-- Pretty footer -->
      </footer>
    </div>
    <!-- Modal #add-done-modal -->
    <div id="add-done-modal" tabindex="-1" role="dialog" aria-labelledby="add-done-modalLabel" aria-hidden="true" class="modal fade">
      <div role="document" class="modal-dialog">
        <div class="modal-content">
          <div class="modal-header">
            <h4 id="add-done-modalLabel" class="modal-title">cart.add.done</h4>
            <button type="button" data-dismiss="modal" aria-label="Close" class="close"><span aria-hidden="true">×</span></button>
          </div>
          <div class="modal-body">cart.add.done.long</div>
          <div class="modal-footer"><button data-dismiss="modal" class="btn">modal.close</button></div>
        </div>
      </div>
    </div>
    <!-- Modal #add-failed-modal -->
    <div id="add-failed-modal" tabindex="-1" role="dialog" aria-labelledby="add-failed-modalLabel" aria-hidden="true" class="modal fade">
      <div role="document" class="modal-dialog">
        <div class="modal-content">
          <div class="modal-header">
            <h4 id="add-failed-modalLabel" class="modal-title">cart.add.failed</h4>
            <button type="button" data-dismiss="modal" aria-label="Close" class="close"><span aria-hidden="true">×</span></button>
          </div>
          <div class="modal-body">cart.add.failed.long</div>
          <div class="modal-footer"><button data-dismiss="modal" class="btn">modal.close</button></div>
        </div>
      </div>
    </div>
    <div id="login-modal" tabindex="-1" role="dialog" aria-labelledby="login-modalLabel" aria-hidden="true" class="modal fade">
      <div role="document" class="modal-dialog">
        <!-- I don't care about this modal -->
      </div>
    </div>
  </body>
</html>

EDIT: Here is a fiddle with some tests. After rendering the document to the DOM, $('#add-done-modal, #add-failed-modal') and $('#add-done-modal') find the modals, before it doesn't. In this second fiddle with static HTML, I checked if the modals were found, they were.

Is there any limit to jQuery context length for HTML strings?

Kunj
  • 1,980
  • 2
  • 22
  • 34
Alexandre Parent
  • 127
  • 1
  • 10
  • What debugging have you done? Have you checked the result of `$('#add-done-modal, #add-failed-modal', data)`? – Phil Mar 07 '18 at 01:38
  • 1
    Do note that `data` has to be an `Element` or a jQuery object... http://api.jquery.com/jQuery/#jQuery-selector-context – Heretic Monkey Mar 07 '18 at 01:38
  • @Phil Yes I did. "The code never find the `id`." `$('#add-done-modal, #add-failed-modal')` never find anything but the `div` are in `data`. – Alexandre Parent Mar 07 '18 at 02:37
  • 1
    @MikeMcCaughan No, it doesn't. It can also be an html string not in the `document`. – Alexandre Parent Mar 07 '18 at 02:39
  • @AlexandreParent where exactly does it say it can be an HTML string? The [documentation](http://api.jquery.com/jQuery/#jQuery-selector-context) clearly states _"A DOM Element, Document, or jQuery to use as context"_ Perhaps you should try `let context = $(data)` and use that – Phil Mar 07 '18 at 02:46
  • @Phil My experience... I do load other stuff from `data`. If you wish I can post the whole function. – Alexandre Parent Mar 07 '18 at 02:48
  • @AlexandreParent you know what, you're right ~ https://jsfiddle.net/tm2w7g6a/1/. I think we're going to have to see what's in `data` – Phil Mar 07 '18 at 02:52
  • @Phil Coming right up... Just editing the generated HTML for brevity. – Alexandre Parent Mar 07 '18 at 02:55
  • @AlexandreParent after you edit it, make sure you still see the same issue :) – Phil Mar 07 '18 at 02:58
  • @Phil I just removed uneeded information. Unless jQuery cannot parse an HTML string 9663 characters long. – Alexandre Parent Mar 07 '18 at 03:10

1 Answers1

1

If you want to work with the HTML string properly, I'd suggest parsing it into a document, eg

const domParser = new DOMParser()
const doc = domParser.parseFromString(data, 'text/html')

then use doc in your jQuery selectors, eg

$('#add-done-modal', doc)

When jQuery encounters a context, it simply converts the expression to...

jQuery(context).find(selector)

See https://github.com/jquery/jquery/blob/3.3.1/src/core/init.js#L99

The issue here is that jQuery parses entire documents into a jQuery object containing the top-level elements under <body>. See http://api.jquery.com/jQuery/#jQuery2

But if the string appears to be an HTML snippet, jQuery attempts to create new DOM elements as described by the HTML

For example, taking your HTML as a string...

$(html)

gives me a jQuery object with 15 elements, mostly empty text and some comment nodes but including

#3  <div id="all">...</div>
#7  <div id="add-done-modal"...>...</div>
#11 <div id="add-failed-modal"...>...</div>
#13 <div id="login-modal"...>...</div>

Using this object as a selector context, you can find elements that are children of those 5 but not those elements themselves. This is why you can find #product for example.

Phil
  • 157,677
  • 23
  • 242
  • 245
  • I didn't know you could create in-memory `document`... Considering jQuery finds the selectors once the HTML is parsed, it may well be the solution. – Alexandre Parent Mar 07 '18 at 04:53
  • @AlexandreParent yeah, I had a feeling it wouldn't. It seems odd though. – Phil Mar 07 '18 at 05:02
  • Second solution works! May I suggest you remove first solution? And your guess is false because it does works for elements much higher in the string. – Alexandre Parent Mar 07 '18 at 05:07
  • Nah, I'll leave it. Like I said, it _should_ work :) – Phil Mar 07 '18 at 05:08
  • I don't see why? I'm almost certain AJAX only use datatype to validate headers. – Alexandre Parent Mar 07 '18 at 05:12
  • 1
    @AlexandreParent Sometimes it uses it to parse / format the `data` passed to the `success` callback (eg `'json'`). For `'html'` though (and I've only just read this in the doco), the result is an HTML string. Why jQuery is unable to use that string as a context, I'm not sure but `$(data)` doesn't result in anything useful either which was the basis for my _"guess"_ above – Phil Mar 07 '18 at 05:17
  • @AlexandreParent I've update my answer with some more info that I think describes what's going on – Phil Mar 07 '18 at 05:33