How to display numbers fetched with JS, but not delay page loading? (async and defer not working)


#1

I need a simple method to display a couple of numbers that are fetched externally using JS, but the method must not stop the page from rendering while waiting for the fetch to complete. The external site the numbers are being fetched from occasionally gets real slow. Sorry, my JS skills are minimal, so I am probably not using the all right terms…

In the header of my pages is a “View Cart” button that also displays the quantity of items, and the current dollar value of the cart. It looks like this:
…View Cart
2 items $43.89

I experimented with adding both async, and then defer, to the script’s below. In both cases the page loaded OK, but the Item Count and Dollar Value spaces on the View Cart button remained blank.

Currently, the button with the item count and dollar value is fetched and displayed with this code:

<a href="https://secure.cart.com/cgi-bin/UCEditor?merchantId=ID"  class="btn">
  View Cart<br />
  <script type="text/javascript" src="https://secure.cart.com/cgi-bin/UCJavaScript?merchantid=ID&amp;type=count">
  </script>
  items
  <script type="text/javascript" src="https://secure.cart.com/cgi-bin/UCJavaScript?merchantid=ID&amp;type=total">
  </script>
</a>

Can you provide some code examples of how to fetch these numbers with Javascript located down below the footer (with the rest of the misc JS) AND then show the numbers way up in the header? If the numbers pop up a little while after the rest of the page is already visible, that is perfectly OK.
(I do need the code examples because my JS skills are minimal)


#2

Just calling the script from the external site won’t update your UI. You’ll have to make an HttpRequest, load the response into a variable, or variables depending on the type of response, then update the UI.

There are easy ways to do this with jQuery or Angular, but as you have minimal javascript experience, getting to know either of those will muddy the water a bit for you. If you can tell me how a response is formatted, I can probably whip up some examples for you.

Also, the ID is being sent as the text “ID”, literally. You probably need to break those out of the string and append them like so:

"https://secure.cart.com/cgi-bin/UCJavaScript?merchantid=" + ID + "&amp;type=count"


#3

Hi JK, thanks for the fast response! I had made the url and id “generic” when I did the post. But I guess that as soon as the page is live that anybody who knows how to do ‘View Source’ will be able to see the little javascript details.

Here is one of the pieces. Can you tell how the response is formatted from this?

<script type="text/javascript" src="https://secure.easydigging.com/cgi-bin/UCJavaScript?merchantid=TOOL&amp;type=total"></script>

#4

Unfortunately, The most response I can get from that is a line of javascript saying I have no cart, and if I try to wrap it in fetch or HttpRequest, I get cross-origin errors. If you can get me an example of what comes back when it does work properly, I’ll try to piece it together for you later.

Something for you to look into:
Using Fetch

That might help you solve it on your own. You should be able to use fetch to grab the data, and in the “then” block update the view, but if you start getting CORS errors (the cross-origin I mentioned earlier), we’ll have to figure out another way.


#5

Thanks JK. I did read the article about Fetch. I only understand a little of it, not enough for me to do anything constructive with. Sorry.

Here is a link to a draft page with the “View Cart” button and JS we are discussing.
https://www.....easydigging.com/kern/product-trial-7.html
(just strip out the extra periods to make it functional)

The View Cart is in the upper right header. You can click the “Buy Now” button a little further down to put a test item into the cart. Doing that causes a snippet of data to be sent to the cart, and then the cart redirects the shopper right back to the original tool page. To the shopper, it just looks like the page refreshes or updates.

That updated page will then have the latest cart count and amount displayed in the header button, and a new box appears at the top of the page telling them that an item was successfully added (along with displaying the updated View Cart in that box, just in case they are done)

If it helps, the top View Cart button code is on line 128 and the Buy Button code is on line 655 when looking at the code using View Source.

Thanks for exploring this. I dug around a bit and could not find any applicable fixes.

Greg


#6

Alright, from what I can tell, you don’t actually need to fetch or anything. It looks like everything you are calling is from within the same site.

That said, I presume that inside the scripts you are using as sources there is more javascript. It might be there that we need to make the changes to fetch, which means digging into the cgi-bin stuff. I believe what those scripts are doing is returning javascript that just does document.write() to put stuff on the page.

Do you have access to those, or is it something like a REST service that is returning javascript instead of JSON?

[Edit, because I ran into something that might be helpful. See below:]
I found this answer at Stack Overflow, which I think might work well for you.

Rather than hosting the script tags directly in the button, try changing the button’s HTML to something like:

<a href="https://secure.cart.com/cgi-bin/UCEditor?merchantId=ID" class="btn" id="cartButton">
  View Cart<br />
</a>

Note that I added an id to it. That’s for the next part, which you can place at the bottom of the document’s body.

<script>
    var cartButton = document.body.querySelector("#cartButton");
    var cartCount = document.createElement("script");
    cartCount.onLoad = () => { 
        cartButton.appendChild(this);
        var cartAmount = document.createElement("script");
        cartAmount.onLoad = () => cartButton.appendChild(this);
        cartAmount.src = "https://secure.cart.com/cgi-bin/UCJavaScript?merchantid=ID&amp;type=total";
    }
    cartCount.src = "https://secure.cart.com/cgi-bin/UCJavaScript?merchantid=ID&amp;type=count";
</script>

I’m not 100% on whether that will work as is, but logically, I don’t see why it shouldn’t. Let me know if that helps at all.


#7

Hi JK.
This is exactly the type of solution I am looking for…one that doesn’t cause any serious problems with page loading if the src secure.easydig… is not available or slow at that moment.

(you mentioned calling from within the same site, and it looks that way, but is not. The addresses www.easydig… and secure.easydig… are hosted in completely different ways. The www is through my hosting service, while secure is through the shopping cart provider)

Anyways, I did test your suggestion (with corrected urls) and no luck. I also tried a few experiments with no success. Such as…

  1. removing the < br / >
  2. removing the id from the <a href and putting it in span around the words View Cart
  3. simplifying by removing the central portion from the script, the part that call the dollar amount, so that all it was trying to do was get the count
  4. I know that the original scripts were working the whole time because these is another one of these View Cart buttons in the footer, and it was displaying the right count and amount the whole time.

Any ideas how to make this idea work? Would it help if I edited the sample page I linked earlier and made the update live to play with?


#8

It would certainly help me if I could play with it, but I didn’t figure that would be an option.

With what I gave you, are there any errors? Or is nothing at all showing up?


#9

Greg,

Is it not normal for a HTML script to have a src with a cgi program. A script should be a static file, just containing raw text about what code to run.

If you want to download data from that cgi link, you need a script that will do an HTTP request to fetch it. You can use XMLHttpRequest or fetch().

So there are 2 requests that you need. One to fetch the Javascript code you want to run in the browser. A static file. And the other request is to perform an HTTP request that will fetch data (for example with the cgi program link).

The most common practice is to have not many but just one script at the end of the body. using head scripts with async and defer are for fine tuning and other tricks.

When you have done the HTTP request, you need a callback that will update your HTML elements.


#10

I don’t think he has any control over the cgi-bin scripts, and I don’t think those scripts want to be fetched. It seems that they look for variables set by previous scripts and return javascript on the fly, rather than a JSON object with the data.

But I concur with what you are saying, it is unusual, and it is not what I would consider good practice.


#11

Hi JK and Tvilmart,
There is a JSON and REST possibility. I did not use it originally because there was not a complete example on how to use the data fetched by this method.

Here is a link to the support page that details the option:
https://ultracart.atlassian.net/wiki/spaces/ucdoc/pages/1376712/Advanced+Links+-+JavaScript

and here are some excerpts from it: (I am the extreme case third-party site they mention)

JSON Cart

For the extreme case where you cannot run a proxy and need to dynamically display an UltraCart shopping cart on a third party site, you may request a REST cart object.

Syntax:

 https://secure.ultracart.com/cgi-bin/UCJavaScript?merchantid=DEMO&type=json&cartvar=my_cart&callback=function_to_run 

Example code:

< script type = 'text/javascript' >

function display_cart(){

if(my_cart){

var total = my_cart.totalLocalizedFormatted; // make it pretty

var itemCount = my_cart.items ? my_cart.items.length : 0;

// now do something with these values...

}

}

</ script >

< script type = 'text/javascript' src = '//secure.ultracart.com/cgi-bin/UCJavaScript?merchantid=DEMO&type=json&cartvar=my_cart&callback=display_cart' ></ script >

The only part that keeps from using this method is th bit in the middle of the example that says “now do something with these values” — because I have no idea how to get those values to appear in my html button.

Is this stuff helpful?


#12

Ah! Simple then :slight_smile:

Change your cart button.

<a href="https://secure.cart.com/cgi-bin/UCEditor?merchantId=ID"  class="btn">
  View Cart<br />
  <span id="cartCount"></span>
  <span id="cartTotal"></span>
</a>

Then in that function where it says "do something with these values:

var cartCount  = document.querySelector("#cartCount");
var cartTotal = document.querySelector("#cartTotal");

cartCount.innerHTML = itemCount + "item" + (itemCount > 1 ? "s, " : ", ");
cartTotal.innerHTML = "$" + total;

Might not even need that “$” depending on whether they’ve included it for you.


#13

When you click on a <a href>, it goes to another page and you lose the initial page.

You need to use the following unless it is part of the framework already:

event.preventDefault();

#14

This won’t require clicking the anchor tag to work, and it doesn’t lose anything when you click on the cart button. The site stores your cart info (presumably in cookies, though I haven’t checked), and clicking the link takes you to the checkout page, as desired. I don’t think event.preventDefault(); is necessary in this case.


#15

I will try this, but I need guidance on where the 3 pieces of code go on the page.

Piece 1 is the html button code (I know where that goes)

Piece 2 is the longer script that starts with " function display_cart(){ "

Piece 3 is the smaller script that starts with " src=’//secure.ultracart "

Should pieces 2 and 3 be added in that order to the very bottom of the page with the rest of the scripts, or put somewhere higher on the page?


#16

Piece 2 & 3 can just go at the bottom of the page, in that order, since that’s what they gave you in the example.

Piece 2 can even be added within one of the already existing tags.


#17

It worked! :smile: …and then it stopped :slightly_frowning_face:

I had it working along enough to tweak the little formatting details like the displayed $ signs and the space before the word item. But then it just stopped. I did contact my shopping cart supplier to see my JSON and REST access somehow got turned off. Should hear back from them in a few hours.

Anyhow, it is live on the sample “trial-7” page I linked to in an earlier message. The “View Cart” button on the right side of the header has the new JSON code. But the one down in the footer still has the old Javascript code (it still works)

The script starts on line 1280 in the source code.

Any idea what stopped working?


#18

Actually, no, I don’t… I’m sorry. Perhaps one of the other scripts is messing with it. Hopefully your cart supplier will be able to tell you something useful.

image

I stepped through it on your trial page and you can see that it is working (and that we need to change the itemCount text just a little), and yet, I also see that the button is not being updated.

Try changing it to .innerText instead of .innerHTML, but that really shouldn’t be the problem.

As for the slight change to the itemCount text itself, change > 1 to != 1.


#19

Actually, found the problem, I think.

Your page has 3 copies of that button, and the first copy is not the one that is visible.

So, we’ll have to make some more modifications.

function display_cart(){
    if(my_cart){
        var total = my_cart.totalLocalizedFormatted; // make it pretty
        var itemCount = my_cart.items ? my_cart.items.length : 0;
        var counts = document.querySelectorAll("#cartCount");
        counts.forEach((count) => {
		  count.innerText = itemCount + " item" + (itemCount != 1 ? "s, " : ", ");
        });

        var totals = document.querySelectorAll("#cartTotal");
        totals.forEach((total) => {
            total.innerText = total;
        });
    }
}

I went with innerText this time just because it’s what we’re actually adding, rather than HTML. You can use either.


#20

:grin: Woo-hoo!! That fixed it! :+1:

I never realized that you had to do something special to use a stored value in multiple places. Still don’t understand why, but I am so glad you caught it and new the fix.

There was one little tweak needed to the code you just sent. The last few lines apparently used the word “total” (singular) in too many places. So I changed one to “dollar” and they now read:
totals.forEach((dollar) => {
dollar.innerText = total; });}}

JK, I owe you big time. If you ever need knowledge or help on anything related to garden tools, ecommerce, or manufacturing - all you have to do is ask. Thank you so much.

Greg