0

I want to open a modal on clicking a link. The link is present in a container/section which is generated through a script tag.

I am loading jQuery, Popper and Bootstrap CSS & JS files inside the script.js (it will be added to third-party websites). The container is displayed with items but the modal does not open.

Page.html

<body>   
... 
<script src="http://localhost:7000/" type="application/javascript"></script>
</body>

SCRIPT.JS (hosted on express server)

Load jQuery

script.src = 'https://code.jquery.com/jquery-3.3.1.slim.min.js';
script.integrity = 'sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo';
script.crossOrigin = 'anonymous';
document.getElementsByTagName("head")[0].appendChild(script);

Add Bootstrap CSS

script.rel = "stylesheet";
script.href = "https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css";
script.integrity = "sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T";
script.crossOrigin = "anonymous";
document.getElementsByTagName("head")[0].appendChild(script);

Add Popper and Bootstrap JS in Body (functions to load respective url, integrity and crossOrigin)

loadPopper('https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js','sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1','anonymous');

loadBootstrap('https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js','sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM','anonymous');

After this, I have my HTML containing the section with the link.

If I move just the jQuery script tag to Page.html the modal starts to open.

Page.html

<body>
...
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
<script src="http://localhost:7000/" type="application/javascript"></script>
</body>

I tried loading the jQuery in 'body' in the script.js (like page.html above), it didn't work.

user3884753
  • 255
  • 6
  • 16
  • Are there any console errors? If I recall correctly, scripts added via JS are async by default. That means jQuery, Popper, and Bootstrap might not load in the right order. – crenshaw-dev Sep 25 '19 at 19:08
  • Most likely, the script that's supposed to add the script tags to head doesn't do what you expect. Without a proper [mcve] your chances of getting help here a quite slim. Also note in the case of CSS, the tag which needs to be added to head is ``, not ` – tao Sep 25 '19 at 19:46
  • @MichaelCrenshaw, not able to find any documentation ref the async default of dynamically added tags. On the contrary, i found various solutions on how to add async scripts to head, which implies they're not async by default. – tao Sep 25 '19 at 20:23
  • 1
    [MDN says](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#Attributes) "Dynamically inserted scripts (using `document.createElement()`) load asynchronously by default." But it [depends on browser support](https://stackoverflow.com/a/5160676/684776). – crenshaw-dev Sep 25 '19 at 20:26
  • 1
    @MichaelCrenshaw I am adding the scripts through callbacks so they load in the proper order. First adding jQuery, then in its script.onload loading Bootstrap CSS and so on. When the final script is loaded, I am including the HTML containing the containers. The containers and columns are being styled properly as expected. – user3884753 Sep 26 '19 at 04:34

1 Answers1

1

When I try to add them with integrity and crossorigin attributes, I get a CORS error for each.

When I remove the attributes and simply specify the src/hrefs, it seems to work:

function addHeadTag(tag, options) {
  let item = document.createElement(tag);
  Object.assign(item, options, { async: false });
  document.querySelector("head").appendChild(item)
}

function avoidFouc() {
  document.body.style.opacity = 1;
}
addHeadTag('link', {
  rel: 'stylesheet',
  href: 'https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css',
  onload: avoidFouc
});
addHeadTag('script', {
  type: 'application/javascript',
  src: 'https://code.jquery.com/jquery-3.3.1.slim.min.js'
});
addHeadTag('script', {
  type: 'application/javascript',
  src: 'https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js'
});
addHeadTag('script', {
  type: 'application/javascript',
  src: 'https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js'
});
body {
  opacity: 0;
  transition: opacity .2s ease-in-out;
}
<!-- Button trigger modal -->
<button type="button" class="btn btn-primary" data-toggle="modal" data-target="#exampleModal">
  Launch demo modal
</button>

<!-- Modal -->
<div class="modal fade" id="exampleModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
  <div class="modal-dialog" role="document">
    <div class="modal-content">
      <div class="modal-header">
        <h5 class="modal-title" id="exampleModalLabel">Modal title</h5>
        <button type="button" class="close" data-dismiss="modal" aria-label="Close">
          <span aria-hidden="true">&times;</span>
        </button>
      </div>
      <div class="modal-body">
        ...
      </div>
      <div class="modal-footer">
        <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
        <button type="button" class="btn btn-primary">Save changes</button>
      </div>
    </div>
  </div>
</div>

Note: I don't know enough about CORS to tell you why it fails when integrity and crossorigin are specified on dynamically added tags.

But I noticed it works without them.

I also noticed this technique causes FOUC, so I added a small function to avoid it (basically hiding document.body until Bootstrap's CSS is loaded).

EDIT: {async: false} added to the script options makes sure they load in the order they were passed. Should be removed if loading order doesn't matter, to improve page load times.

tao
  • 82,996
  • 16
  • 114
  • 150
  • Hey, I followed a similar approach to yours. Converted the 4 function calls into Async/Await and inside the AddTag function created a promise and resolved it when 'onload' happens. This made all the calls to happen in sequence and the modal started working. As you mentioned, this does not work with integrity and crossOrigin added. Did you also mean Async/Await or Promise based approach in your answer? Can you modify your answer to include Async/Await so that it is clear for anyone else. I'll accept your answer. :) – user3884753 Sep 26 '19 at 11:40
  • I am trying to add this script to Shopify store via. On my standalone HTML page the code works as expected. But on the Shopify store after clicking the button the screen greys out for a fraction and again becomes white. Modal does not open. Would you have any insight as to why this might happen? – user3884753 Sep 26 '19 at 11:44
  • adding `{async: false}` to the tag generator will take care of the order, @user. Updated the example. – tao Sep 26 '19 at 12:40
  • Apparently adding scripts to shopify is not straight forward. See [this](https://stackoverflow.com/questions/54453671/how-to-add-script-to-the-front-side-of-the-shopify-app) question. – tao Sep 26 '19 at 19:44
  • Thanks again. I have already added the script through the Shopify API. The view is rendering correctly on the store. Just the modal is not opening after clicking. – user3884753 Sep 27 '19 at 05:18