[CFCODELAB] - Custom Javascript Part 2

This video session (Jaime Smith with Dan and Jeremy) demonstrates advanced ClickFunnels front-end customizations using JavaScript and jQuery. Major demonstrations include: an order-summary add-on that detects checked products, computes quantities and subtotals (supports CF Cart mode), clones a native summary row template, and exposes a window variable for Facebook pixel tracking; conditional quantity-column display when Cart mode is enabled; a three-step order form UI (show/hide steps, scrollToTop helper) and AJAX CFAR-submit flow that serializes the hidden CFAR form and posts to the page to create contact records without redirecting (enables abandoned-cart followup); Promo Code Pro that gates the promo input via URL parameter (pcon) and swaps product templates for discounted pricing; a demo of JavaScript obfuscation/de-obfuscation; and a multi-product-selector pattern mapping product-ID arrays to separate selectors, with debugging guidance for adapting CF Cart mode to multiple selectors. Practical automation advice: use the "saw page but didn't purchase" event with a delay (e.g., 15 minutes) before sending follow-up emails.
Chapters
I'm actually going to just start with that. I'm going to go ahead and share my screen and we'll run through the OFA challenge stuff first and then we will jump in and get to Jeremy's question about setting up product pages where you might have categories of products that you want to have use a product selector for with drop downs. I've got a pretty cool example of that and it's actually part of the our funnel that we used at Funnel Hacking Lab a couple years ago to be able to select from different packages. But it's all products that are creating clickfunnels. But you get kind of that exact same thing. It's a three column. Hey, if you sign up here, we want you to pick this thing first. Here's the drop down of that stuff. Pick this thing next, here's a drop down of that stuff. And then pick this thing last. And here's the dropdown of that stuff. And they're all products and they're all using product selectors within clickfunnels. So can you guys see my screen? Okay, first, let's make sure of that. Hopefully that's okay.
Yep, I can see it.
Okay, great. So I've got the One Funnel Away challenge here. So what I'm going to do is I actually just. If you're using Mac and Chrome, you can hit command option U to view source or you can just go to. And hopefully you guys all know this by now, but you can always go to the View and developer and then view Source and then it's going to pull up the actual source code of the page. This is the source code. This is probably one thing to mention this. There's a couple ways to view source, right? You can actually right click and do inspect and it will pull up the Inspector and the Inspector in Chrome. Slightly different in Firefox than what it is in Chrome, but they're close. But in this case, this is just an HTML page. It's got JavaScript on it. But the code that you see in here, this is the live view. So this is the document object model view, the DOM of the current page. This is after JavaScript is fired.
So.
So there's a lot of things that you can inspect on this page that you'll see in the source code here. But then if you were to go and view this source code, you would never find them because they're actually generated on the page. After the page is loaded, there's JavaScript that fires and says, hey, insert a bunch of these elements or take These elements and modify them. That's the source you see here in this Elements Inspector is the modified version after the page is loaded. But what you see here is the code that gets downloaded and is never actually processed. So this is the original source code. Same way here. If you right click here and do just View Page source, that's doing basically the same thing. It's open in this page source, but this one I use quite a bit because it's a little easier to actually grab the actual source code for the scripts that are on this page. So I'm just gonna grab all the code here. So I'm gonna scroll up CF Pro Tools code. This is also something we didn't cover last week. The reason that I use, if you ever have inserted anything from safetro Tools, you know that I always put everything except for one add on where I had to use the orange box and that is the PayPal plus add on. There's a very particular reason for that. But all the other add ons are inserted into tracking codes and footer codes. And the reason they're in the footer codes is because it puts them at the bottom of the page and they all use jQuery. And jQuery is not defined on this page until about halfway through. So it's not actually pulled into clickfunnels until about halfway. So if we put them into the header codes, the jQuery code will actually be called before jQuery is actually loaded and it will bomb out. So you'll find all the custom code that I use for CF Pro Tools always towards the bottom of the page. So I'm going to scroll through here until we find the top of it. We've got a lot of it in this page. So we can see right around here we start to get the actual JavaScript includes from ClickFunnels itself. So when I start seeing this definition for this code here, we start seeing our Stripe includes, things like that. That's when I know I'm getting close. And then we got a little chunk here that's related to Stripe and Apple Pay. So what we want is this starts our actual custom code right here. So I'm going to grab all this and drag it down and I'm gonna go all the way to the bottom and then I just need to grab this script tag here. So that's all the custom code that we've inserted using the footer codes. So I'm gonna go ahead and just pull this in. I'll paste it into a brand new window and are you guys able to see the size of this text? Okay, do you need me to blow it up at all or is this large enough?
Actually, it might be too big.
Might be too big. Okay, let's. Let's see if we can.
I got a 24 inch monitor though, so.
Okay.
Yeah, I can shrink my screen down. Yeah. Leave it that size for people with smaller.
No, actually that looks great. I've got it on my MacBook, so that works. Okay, cool. Okay, great. Yeah, and I gotta be able to read it here too. And I've got two 21 inch monitors, but my eyes are getting a little bolder and I don't have my glasses on yet, so I'm actually gonna get my. I don't often wear these when I'm on camera, but I'm gonna go ahead and put them on since we. I normally wear them when I'm coding. I got my little, my, my gunner optics glasses that they don't magnify, but they reduce eye strain a little bit and make it a little easier to see with all the contrast in here. So what I want to do is just kind of walk through some of this code and the first chunk here is. We talked about this last week. I'm including the handlebars library. So handlebars is what we see when, if we want to on the page. If we put in City, then that's going to look for a data element, a variable city that's included in a certain array and it's going to replace that text value. It's going to look for that on our page somewhere and it's going to replace that value with whatever data we have that's named city. And so because of these kind of curly braces, we also call these sometimes mustaches. So there's actually another library called mustache that replaces these things. So this one's called Handlebars and I use that. It's pretty reliable and I know it and it's included in a lot of places. Unfortunately, it's not included directly in clickfunnels. So we include it at the top of our code here so we can use it later. And then the next thing Jim was just talking about, when you're including those like I know in the members area like that, you will put those libraries with every script that uses them. When you're actually, you know, inserting them into a page, will you only add it once? Yeah, so if I know that, yeah. So if I'm using several add ons that I know use the same library, I just include it one time at the top. I just include it with each one that adds it because I don't know if somebody's gonna use multiple add ons on the same page or just stick with one.
Right.
So, yeah, so in this case, you know, if somebody were to include it multiple times, it's not really gonna cause any problems, but definitely you don't need to. And because it's cached, it really should cause a very, very minimal impact on load speed. Because when you call it from. Especially from the cdn, I'm calling it from the CDN here to include should be a minimal impact impact on load time because it already. Your browser's already gotten that file one time, so it's not actually gonna have to go out and fetch it again. So including it multiple times is not gonna have any impact, but definitely don't need to. So I would just include it one time to the top if you're gonna use it by several different add ons. And then our next chunk down here is actually the code that grabs. You will notice here that we're setting a spreadsheet id. We've got this long string characters here. And then we make a call out to spreadsheets.google.com so we've got a URL set up for that. And so this is something that's. That's pretty cool that you can actually do this with any spreadsheet with any Google Sheet. And actually, let's just walk through this real quick so you can kind of see this. I'm going to open up a new window. We'll go to Google Drive and we just say we want to create a new. Actually, let me. Yeah, we'll just create it here. We want to create a new Google sheet.
So this is what you're using to be able to do what you said you're doing with the OFA to set it up every two weeks and then run it through your multiple funnels and everything. Okay.
Yep, exactly. Exactly. So we'll let this finish loading here. And so, you know, let's say, you know, we want to do something like cart close date. That's one of the pieces of data that we want to be able to reference. And so we're just going to create a quick spreadsheet and then I'll just walk through how to actually publish that. So you can kind of use that in the future if you want to. So we want to be able to say, okay, when is the cart going to close? So we want a date for that. And then when is the next challenge, start, start, not state. There we go. And then trying to think what else we might store on there. You could use things like, you know, cart redirect. You know, if you wanted to say, hey, I always want to, I want to be able to change maybe what page they go to after the cart closes. You know, there's a ton of different things that you could store here, but the big ones that we use for, you know, for the OFA challenge, obviously is when does the cart close and when does the next one start? And those, those are going to be date strings. And so you would store something like, you know, car close, date is going to be what, the 13th? I think we do that. So it's going to be 1013, 2000 or 2019. And so. And then one thing you'll want to be aware of is that in Google Sheets, they want to try and make these, convert these things into strings that aren't always the friendliest. So depending on how you do this, you might want to actually set the format on this to always be, always be text. So you can go through and see it's converted it to automatic. We would just make it plain text. So you can do that and then you can say next challenge start date is 1014, 2019. 2019. While you're laying that out, Jamie just. I actually was able to use this script on a page and have it pull my data from a WordPress site. Oh, nice. Events that update. So, you know, you have the event dates as well as the location for the next event which might move around and you know, because I can, I can schedule those updates in with the customizer and WordPress at a certain time, you know, that next event information changes on my WordPress site and it also pulls that data into ClickFunnels page to be able to save those. That's awesome. Yeah, this was a powerful thing. And yeah, as we were thinking through how could we make this work, I'm like, yeah, that totally could be a thing. Let's do that. So it's basically as you're creating a custom database behind the scenes is all we're doing. It just is an easy way for other people to manage that versus going and creating a SQL database where the majority of people on our team wouldn't have any level of comfort updating data if we just had an actual SQL database or something or something like that running. Nor did we want to take the time to actually host those sorts of things or provide some sort of scripts that's actually going to deliver that data outside of the database and make the call. So I thought, you know what, Google Sheets is easy. Everybody understands how to use them and update them. They're pretty comfortable with them. Google obviously is a robust hosting platform, so it's pretty reliable. And if they give us the tools to test type, this is test publishes JSON. When they give us the tools to actually publish these things in the formats that we want to consume them in, then, yeah, we might as well take advantage of it. So, yeah, so we've got this now. So we've got, okay, cart close date, next challenge, start date, and the cart redirect. I don't think we actually use anything like this in ofa, but this would be something like if you said, hey, I want to close the cart on a particular day and I want to redirect them to somewhere based on when they reach that date. Like if you had a countdown timer, so you could use it in a combination, you could take your countdown library, put a countdown on your page, and have that page when it hits an expiration, you could actually set the on expiry of the countdown itself with the URL that you want to redirect to when it expires. And you could have that date automatically calculated based on this value using a combination of moment js, the countdown js, and this code to actually pull the JSON and make your entire page be completely dynamic. So if you wanted to have a webinar that runs every week and it runs live, and all you want to do is update and you have maybe say you have 15 different webinar registration pages, and you want them all to function based on an exact date that you update in one place. This would be a good option to do that. So that's kind of how we set the data up. It's pretty simple. You know, I think everybody's pretty familiar with spreadsheets and how they work. You do want to set the columns at the top because these are the names that are going to be used in the JSON to identify the data that's associated. So this won't actually be a row in the, in the JSON structure. It expects them to have headings, and then the first row will be this data here as we look at it. But now if we go and say, hey, I want to publish to the web. So you go to file publish to the web, and then we want to publish the entire document. You can also say, I just want to publish a particular sheet. And then you've got different ways you can publish, but we wanted to leave it as web page and then you could expand these down. I don't think there's anything we need to change here. Yeah, we don't want requires. We don't want to require viewers to sign in. This is going to be public and we don't. And we do want to automatically republish when changes are made. So Google will manage all this stuff. So the default settings are pretty good here. And then we just say publish. And we say, yes, I want to publish this selection. And it's going to give us a URL and so we can actually copy that URL. And then let's go ahead and open up a new tab. We'll put that in. And then we can see from that we get a long string here and what we want to do. If we go back and look at our code, we can see that we've got a little bit different way to consume this. So this is the structure of how to get to this thing with as JSON. So we notice at the end it says alt JSON. So that's the alternate. Which alternate version. You could get this as XML as well. If you wanted that, you could change that JSON to XML and you would see this being published as xml. So we can actually take this and we can go back and we really don't even need this tab. We can open a new tab here. I'm gonna paste that in. And then what we need is this spreadsheet ID right here, which is this. So let's get rid of this. What was that? We've now published it. And this is our spreadsheet ID right here. And so if we just grab that and copy it. And then I'm gonna go in here and I replace my JavaScript variable with.
That value.
And I hit enter. Then I've got a JSON extension on my browser that's kind of formatting these things pretty normally. If you don't have that, you'll see this all be just kind of gobbledygook all jammed together. But we can see we've got a JSON data structure here and you can walk through. There's quite a bit. You just have to kind of understand what things mean. But you'll notice what we have down in here in this entry. So this is like the first row each. And we'll notice these are square brackets we talked last week about. Square brackets represent an array, curly brackets represent an object notation. So entry is an array of objects. And then we kind of loop through that and let's so we kind of loop through, we've got our feed here and we can even see this as we look through here. So we say, hey $getjson as from this URL. So this is what the code's doing. It's basically saying calling out to this URL and saying, hey, grab me the JSON. The reason we use this getjson, this is provided by jQuery. This is one of their built in functions. Use this getjson because it understands when it consumes this URL that it's returning a JSON data structure and automatic parses. It automatically parses that data, converts it into a JSON object that we can then reference using array notation or even object notation if we wanted to. But because we've got special characters and all kinds of things here, we want to use actual array notation to get to the values. So we can see here that we're going through the data that we reference here is what's returned to this function. And that's what gets passed in from the get JSON is it passes in and says, hey, go grab this URL. Whatever you return, parse it into a JSON element and then pass it into the function that's called within my second parameter. So data represents that returned parsed JSON object. And then so from there we say, okay, data feed. So that's our next element. So we look for the feed element. So here's feed. And then within feed, look for entry and 0 in this case is our first entry. And so we go down from feed and we look for our entry. And now entry is an array. So we grab our first entry, which is what's included here between the square brackets. So that's all of this is the first entry. And then within that entry you'll notice we have this GSX dollar sign. So these represent each one of our columns, the GSX dollar sign. And it says cart close date, next challenge start and cart redirect. So you notice if we look here, those are our column headings just stripped out and all lowercase. So that's what's gonna happen when you, when you update these things. And you can add more columns if you wanted to. And we'd see this update and include more of these GSX dollar sign keys in there. So it's basically creating a new key for each row and each column. So each column has its key and each row will have a new data element within that. So we can see that these are just being stripped out and all lowercase. So we've got Cart, close date and that gets converted into lowercase cart, close date here. And if you, you know what, the best thing to do is just actually go and create your spreadsheet the way you want to and publish it and then go and look at the JSON, get the JSON out and see, okay, how is it actually named. And then let's just do another example just so we can kind of see how this works from here. So we'll do 10, you know, so it would be one after that 14 days. So we do 27/2019 and we'll do 10, 28, 2019 and we'll do Yahoo.com. does anybody still use Yahoo.com? i haven't been there in years. It's always my page that I go to if I'm trying to make sure my caches. My Internet connection is working well and I'm hitting a non cache page because I haven't been to it in years. So now we've got another entry here. And so our changes were just saved. You know, Google does this great job of just automatically saving our changes. Go. So now if we're, if we go into this and refresh our page, we will see another set of. Now we have another entry. So we have, this is our first entry. And you'll notice we've got a comma here. So this represents our first entry. And then when we have that comma, it's our next entry. So now we have a whole nother entry. And so we've got that. And then here's our closing square bracket down here. So we've got another object. And so now we've got our second set of data. But the same thing. They've got the same keys, GSX dollar signcard, close date. And then the dollar T represents what's the text value of that, of that column or I'm sorry, yeah, that, that row, that cell in this spreadsheet. So we've got 1027 here, we've got 1013 up above. So that's what the code is going to do. It's like, okay, I expect the data to be in this structure. So let's walk through and get the data we need. In our case, we know we're only going to have a single set. We're going to have a single row that has the values in it. We just need to grab our next challenge date, our cart close date and our cart close 24 time. And I honestly forget what we even use that for. But we'll walk through the code and see what it does here in a second. So in this case we say we want to grab the feed dot entry and we want to grab the zero index, which is the first row that's available again, JavaScript. Our arrays are zero indexed if you're just using numerical arrays. And so that can throw people sometimes if you grab one in this case we have. Nothing would be returned because there isn't a second row. It's always our first row. Even though in the spreadsheet we have, we do have a row one, but that's our headings and those actually get stripped out by Google when it publishes as JSON uses those headings to create the keys. And then our rows start at row two and down. So we just say, hey, go out and grab my JSON. And then from the data that's returned, grab, go and find my feed. And within that find my entry and grab my first entry and set that as to a variable named feedrow. And then from that then we say, okay, I want to grab a next challenge text. And this is just returned as text. So we're gonna have to just work to convert it into an actual date that we can do calculations on. So grab my next challenge text value. So I just call feedrow and then I say from that feedrow, which is just an object. This is, this is my feed row here. So I've got my feed row and it kind of starts from right here and goes up and then from that grab the key that is GSX$. Next challenge date. Our columns are named a little bit different here. So we have car close date or next challenge start would be the one that we'd be looking at there. So next challenge date would be we call next challenge start grab its $t that's within that. And so we grab, you know, the $T here. So it's going to grab the 1014 value and it's going to set it into that variable. Same thing. Cart close. It's going to grab the cart close date. It's going to grab its value, it's going to set it into that variable. And then car close time is going, oh, this is, this is what we're using this for. So this is the 24 hour time. So this is, we're using this to set what, what actual time in 24 hour increments. You know, what hour are we actually closing this out? I think we might even insert it as like, you know, so I think that we usually do like 2100 and then we're using that to figure out what time on what date? So we've got a car close date, and then we've got a cart close time within that date. We set those things independently. So this is going in. And then we want to actually convert that into a date so we know we've got our next challenge text. And we've got a cart close text and cartclose time. We're going to convert those into actual dates that we can use. So we go through and we say, okay, so I'm going to create a date. My next challenge date is going to use the moment library, and it's going to use the TZ function on that, because I want to set the time zone to be eastern time. So I set this as America New York is eastern time zone. And this is the format that I want to use is mm, dd yyyy, which is the date format that we're using here. 10 represents month, month, day, day, year, year, year, year. So we grab. We're telling the moment that this is the format that my text is in so that it knows how to parse that text properly and convert it into a date. And. And then I just pass it in the actual next challenge text. So now the moment library knows that, hey, the. The string that I'm bringing in here is in this format, and I need to convert it to a date in this time zone, so it automatically will do all that. And then cart close date, same thing. Only in this case, we're bringing in the cart close text and the cart close time. And so, and we're also bringing in the time zone. So we're saying, hey, cartclose text is the date cartclose time. We're expecting that in our hour colon, minute, minute format. So that 24 hour time. So we can actually do that. Let's add a new column here. Let's see. I'm going to right click and insert one left. And we'll just say cart close time. And then we're actually going to put this in as 21:00. And that one looks like it did say text. I always like to go and set the formatting. Yeah, it's already to plain text. Okay, great. And so I'm gonna go ahead and delete this row so it changes our structures to be a little more what we expect in our code. And then if we go out here and refresh, we should see this change a little bit. So now we've got our cart close time, and we're back down to having just one feed entry. And so then that cartclose time says okay, so this is gonna end up with the cartclose text being a string combination. So this is another thing that we didn't talk about last week, but it's good to know. So we have text here and then we use the plus symbol and then we have single quotes with a space in the middle and then we have another plus. So this is a concatenation operator. The plus sign is not actually doing math in this case. It's actually just saying take this string and add this other string and then add this other string. So this is a. The plus sign in this case is a concatenation operator when you're combining. And JavaScript will look at any two things. So you can, you can. JavaScript is strictly object oriented, so it works and looks at things and what type they are. Should say it's a strictly typed language. So objects are a type of object and strings and numbers specifically integers or floats. Float is another type of decimal point number inside of JavaScript. Those things don't necessarily work together all that well, but you can add them together, you can concatenate them. And what happens is if you take a number, say we were to have 21 as a an actual integer number. Like if we were to say var close our equals 21. Notice no quotes around that. It's. And it's highlighted in yellow that tells us this is actual represented as an integer number which. And you can't necessarily add a string and a number together because it's going to give you weird results. But what happens when you use the concatenation operator when you've got a string that's already associated here and you use plus. If we were to put plus 21 here, then that's not gonna try and do math on 21. It actually just converts 21 into the string representation of that number, which is 21. So you'll end up concatenating the number 21 as a string onto the already existing string. Otherwise when you've got variables, when you actually got numbers. And we say, you know, that number is 22, then if you were to say, you know, say something like, oh, there we go there. Math equals close hour. So in this instance the plus is actually a math operator because we're working on numbers here. So when we've got strings, it's a concatenation. But when you've got actual numbers, in this case, both of these, both sides of this operation are integers, then the math knows it can add those things together as actual numbers and it will return another number and so in this case, we'd end up with 43. The math would be set to the integer value of 43, not the string value of 21, 22. And so that's just something to be aware of when you're using the plus sign and knowing what types of variables you have that are on either side of that operator so that you know what to expect to come out. If you've got a string and a number, you're gonna get a string returned. If you've got two numbers, you're gonna get another, you know, and actually return a number. The other thing to keep in mind is if we do 22, 21 plus 22.0, this 22.0 is a float. And so this is an integer. There's no decimal point in 21. So this is 21 is an integer, 22.0 is a float. So now if we added these together, we would. Even though the math would be set to 43.0, it now has a decimal point. So it's going to carry forward and say if you add an integer plus a float, you're going to get a float. So just a couple of things to keep in mind as you're doing math. And there's some things that you can do on integers that you can not do on floats and vice versa. So it's just important to know, like what kind of data am I looking to return? And making sure that your code is expecting the right thing, especially when it comes to strings. And so you want to, you know, kind of keep those things in mind. Strings and numbers, integers specifically, and floats specifically, so that you kind of have those things in mind. So a little tangent there. I just remembered we didn't actually go through the use of the plus sign here. So in most cases we're going to use that for concatenation. Sometimes you'll use it for math if you need to. But in this case, we're concatenating and we want to say the format we expect is month, month, day, day, year, space, and then hours, colon, minutes. And that's what we have. We have our close date brings us the month, day, year, and the car close time brings us the hours, colon, minutes, and we have a space in the middle. So those the variables we pull in. We want to create a string representation of that. And this is the format of that string. And we want to set the this to be eastern time zone. The one thing to note with moment is the moment time zone Moment library works off the local browser's time zone. So all you said was. Give me the current moments, it's going to return that to the user and whatever their current time zone is. So in this case, I'm in Mountain time. So this is going to return me a moment that's in Mountain time. But if I want to force it to be Eastern time, then moment is going to. And then I have to use the TZ and I can say.
America, New York.
And then if I were to output that time and format that time, then it would do the math to actually add two hours to that for me, because I'm in Mountain time. Eastern is two hours ahead. So do the math to output that and have the appropriate time. So moment will help to do all the math for you. And it keeps track of things like daylight savings time and all that.
Did you want those two brackets after moment between moment and TZ in this case?
Yeah, it will still work either way, but that will work just as well. So with this moment here, it actually represents calling the. Calling the constructor of moments, which I believe will work just as fine in this scenario. But moment here is actually calling object moment and calling the TZ function on it. So that's good call. And we can actually try that. Let's see. Because I could be wrong, but this is the beauty of what we went through last week is that we can actually use our JavaScript console. We just have to find a page where we know moment's already included. So we know it's included on any clickfunnels page. So that's why I always just go to cfprotools.com and use that as my jumping off point, because I know that page is going to have the moment library already included. So we'll pull up our console.
And.
Then pull up our console. Here we go. And let's get this one banged out here. And then we'll just paste that in. And so we'll do it. Let's try it first with the parentheses and see if it screams at us. And let's output now. And so no, now gave us a moment. So this returns a moment object so that we can see that, we can expand it down. We can see that it's given us our time. It says, let's see, Saturday, October 12th, 7:05, Mountain Daylight Time. Which is kind of weird. Yeah, 7:05. So it definitely did some math on that. Yeah. And it's set. The time zone is America, New York. So same thing. If we were to do moment dot. Well, here, let's just paste back in what we Had. And then we add. Yeah. So we do it this way without the parentheses. We get undefined, which is just the return of the value. So we end up the same way. We get a structure there. And it should be the same structure. Nope, this one actually did different math. I don't know why we ended up different there.
Different time zone. Because it moved forward, obviously, you know, 40 seconds or so, but.
Yeah. Yeah, but it also moved forward. Yeah, a couple hours. Yeah. So did give you a slightly different time zone. So. Yeah, so we'd have to run through and actually see if we've got. Else looks to be the same. So I'm gonna expand this out a little bit more. No, we don't want to do that. So. Yeah, so that would be. That's actually interesting. I think the. The way we want to call it is. You're a good call. We want to call it this way. And that's what we've done here and here. Moment is probably doing some work on it first. And then adding the tz. We probably wanted to actually do the TZ first and actually call the. The TZ function here. So good call on that one. Which is actually interesting and good to make note of is that they both work and they might seem to return the thing, but they might be slightly different variations. I'm kind of actually very surprised that they're.
Well, actually, the one with the two brackets had the right time zone because there's nine Eastern. That would be seven Mountain.
Or actually it's actually one Eastern right now.
Oh, okay. I'm sorry. I was going off. I was thinking it was the 9 o' clock that the thing expires, but that. That had nothing to do with what.
Oh, yeah, yeah. No, on this one we're just saying, hey, grab the current time. So moment. Moment will always just return the current time in. And it should be in New York Times. So I'm kind of surprised that that's throwing things off there, so. Because if we just go in and say, hey, return the current moment, then this should return to me. Yeah, 11:08, which is the current time. 11:08 Mountain Time. So the time zone there is being thrown off a little bit. And then let's see what moment actually returns. If we return moment here, if we just call moment, then we get. The moment in this case is actually just the function. And so if we do moment TZ America, Boise, and we get an object and let's see what it returns to us. Now it's returning 509 Daylight. I don't know what moment's doing there. Yeah, that's sort of weird. Let's see here. Yeah, there we get the same thing. Which is. Which is kind of interesting. So that'd be interesting. Play around. I think it has to do with this -6 because that would actually, if you added the 6 back to this, that would be the 1109. So I'm not quite sure why that's off. I think it has to do with the. When we're parsing this, it's expecting us to be parsing, parsing a string that is given in a time zone. So if we were to do this and say, okay, so it's. The moment is 1012, 2019 at 1109. And our format is in. Month, date, last year, year. There we go. And now if we say, okay, that is our current time and then it's going to return us a now if we put out now, then it should be. Let's see what we get now. We're still getting out now. It says parses it in your work. I know. It gives us Our date parts 1109 gives us our time zone. Yeah. And you'll notice this and the issue may be here that it's actually converting that, representing it in. You probably have to inspect down a little bit. And this. It's been a little while since I've dug into this deeply. I think this is all time zone stuff. It may actually be a little bit deeper. This may be actually just a representation of current time based on this -6 and saying this is dash 11. So maybe doing some conversion on this internally that's not quite right. So. So yeah, I don't want to spend too much time digging, digging down that rabbit hole. Just know that as a general rule moment works pretty well. And in this case these values return appropriately. And so this is the appropriate way to call them and setting your time zone. Because we always set our spreadsheet in what time we want, what date we want it to close in Eastern time and what time we want it to close in Eastern time. So when it combines those things, it returns and converts them in eastern time. And then the local browser is able to do the math from. And we use that. And as we go down, we'll walk through this code that actually says, okay, what's the current time? What's the current moment and compare it to our cart close moments. And if they're the same or less than each other, then go ahead and let the countdown continue. Otherwise have it fail out. So yeah, so let's Kind of continue walking down this code a little bit. Let me actually clean up some of the stuff that we've got in here. Here we've got just a couple console logs. So it's outputting the format of those things. And so we can do that. And actually let's. If we just do now format, we can kind of see what that looks like.
So those console logs, they don't actually do anything. That's just for the. The coder to go in and see if it's working, right?
Yeah, absolutely. It's just a way to kind of output some code. Sometimes it is good. Like if you. You can actually do some pretty cool stuff. If we go to Facebook and we right click on Facebook and we do Inspect and we take a look at our console once it finishes loading and take a look at our console, you'll notice this. This is all just output with console logs. Stop. It's a browser feature intended for developers. See this for more information. So this is Facebook's way. They have JavaScript code that's written to output these statements using Console log, and they actually format the code. So if we look at this and click and take a look at their code. And so we can pretty this up. This has gone down into a big rabbit hole, but there's actually a way you can. You can call console log. So let's just do a quick Google. So again, stack overflow. You can actually put in and do some extra things. Like you can actually add, you know, background colors, font sizes. You know, there's a few things that you can do. And there's probably Google styling output. So we can take a look at what this looks like. And so here you have some examples of that. So you can actually output some of that formatting. So that's what Facebook does because they, you know, they want to be able to protect their users a little bit. So if somebody's trying to scan them and says, hey, I need you to go into. Go in. Right click on your page and go to Inspect and then tell me what you see here. Or write, you know, put this code in so that it's gonna find something. You know, that that's a feature that scammers might want to use. So they're just trying to warn users here. So console logs can be used for things like this as well. But in this instance, we're just using that to actually output a statement so that we can do a little debugging. And so that's all Console Log does. So we've got console logs in Here. And these probably just didn't get removed. They don't hurt anything. Most people are never gonna see them. They're just. In this case, we're just spitting out the format of the dates, so they could easily be removed. I usually put them in while I'm debugging code or while I'm writing code so I can see what different values are along the way, make sure that I'm getting the right thing, and then I'll remove them when I'm done. But sometimes they don't always get removed. But no harm, no foul. So we can clean those out. And then, so what we're do here in this case is we've got the $getjson. This is what's called an asynchronous function. So this is going to happen and it's not going to. The rest of the code is going to continue on. It's not going to stop the rest of the code from processing. Getjson runs asynchronously. So it's not going to stop the rest of the page from loading while it waits to grab this URL because that might take a few seconds. It doesn't want to stop everything else. But what you can do then with, with that asynchronous function and there's documentation on. If you go and just look up jQuery, get JSON, you'll see the documentation on this works. One of the additional functions we can call on this and we, and we call this chaining. So you'll notice we, we don't have a semicolon here. We didn't end this statement. We just continue on. And then the next thing down is dot, which is another function. We could do it just right here as well. We can do a dot. Done. We're calling an additional function right after that. But I just put it down on a new line here to make it a little more readable. When I say, okay, when that, when we're done with this call, when that function has operated and we've got the data back, we've processed it and we're done with that, then I want to call this other anonymous function. And inside of this anonymous function, we call two more named functions, swap dates and set the cohort field. So we first grab the JSON and when we have it, we do all these things. And then when we've got those things set, because now we want to use these variables in these functions. But if we just called set these functions, if we just called them set right after this, then they get called and we wouldn't have the variable set yet. So if we didn't do it in that dot done, then we'd end up running functions, but we wouldn't have actually have the data yet because it takes us a second to grab the data, so we need to wait. So that's kind of what this is doing here. You'll notice the same thing if you were to use $Ajax or $post or $get. Those are all called Ajax functions. They're asynchronous JavaScript functions. So they happen, they don't stop the rest of the flow, the rest of the code from being executed. They're gonna go out, they're gonna make a call. That thread continues to run while everything else continues. So that's why we have these. These additional functions we call after that. That when that. When this asynchronous function completes and the call has been made and the code within it gets completed, then what are we gonna do? And that's when we actually call the functions to actually use this data. That's what we have happening here. We've grabbed all the data, we've set our variables, and then now we want to.
Jamie, I'm sorry, one quick question here. These named functions down here, so you got the set cohort field right below this. So that won't run until it's called with what you're doing here with the done function.
Yep. Correct. Yeah. These functions, even though they're in the code, the function will never get called. This code will never get executed until somebody calls set cohort field, like we're doing right here. You can. You can include a bunch of functions, and they will never do anything on the page unless they are called by something.
Right. And you didn't do it on page load or document load or anything like your window load.
Exactly, exactly. So this is just a definition of what this function should do. But if we don't actually use it, then it's not gonna do anything. Just like if you're driving your car during the day, we've got a definition for what headlights should do when I hit the button to get turn them on. But if I don't actually ever hit the button, they don't come on. Same thing here. We've defined what this function of set cohort fields should do. But if I don't call it like I do here, then none of this code ever gets operating. So that kind of makes sense. Cool. So but we say, hey, once we've grabbed our data and we've set our variables we do want to go ahead and call those functions. So we're going to swap out the dates and that actually swaps the dates on the page. So that's where we're using handlebars and so we'll walk through that and then we're setting. I mentioned that we have a hidden field on the page that we want to grab the value and to know what cohort should this person be in. So we can actually store that onto their contact profile record. So we store that as a custom field that's actually hidden on the page. The user can't see it, but we know what that value was so that we can then use it in merge tags inside of our email system. So we can send a timestamp, we can send a cohort date as part of the emails. And then our redirector pages that are linked to from our emails know, hey, what cohort is this person in? Ah, now I know what, what page I should send them to. Because we have another spreadsheet that the redirector pages use to determine which Facebook group are they going into. So each the way that things work with the OFA challenges, every time we open a new challenge every two weeks, there's a brand new Facebook group because it's a group that's only open to those people that have just started that particular challenge. And then we cycle through and those are open for a period of time. And then at the end of the challenge, they get archived so that people can continue to see the content, they can't continue to post or anything else. And then after a period of time they'll probably get removed.
Another quick question on these two functions, do they run simultaneously or does swap dates run first and then it comes back and then runs set cohort first?
Yep, they run in sequence. So in this case they are sequential. They're procedural is the word we use for that. So swap dates will run first and then set cohort field will run after. That's when this one has completed.
Okay, I just want to make sure that see if they're running at the same time or not. So you can run swap dates and then that could return a variable that you can put into the next one and then.
Okay, yeah, absolutely.
Thanks.
Yeah, the way that they're set up here, the other thing that we can do, if you wanted to run something asynchronous, so these run synchronously, meaning they run in time. So this one runs first, this one runs second asynchronously means, hey, call this and let it Run, but then immediately continue right on. So what we do here is actually have this swap dates, return what's called a promise, and then that just says, hey, when we return a promise, that means we're not going to wait on this thing to finish, but when it does finish, then go ahead and do something else. So I rarely, rarely use that. It can come in handy. It's actually very handy in something like this where we're calling something that we know is going to take a period of time to run and everything comes after. It doesn't really matter if it's already finished or not, but. And that's why we have this done is a promise that's returned that we can then call as well. But I rarely use that outside of the ones that are kind of built in. Doing our build in asynchronous functions like our AJAX calls, we're getting data from somewhere else. Those are important. But rarely do I use build my own functions that actually return promises that can run asynchronously and do all that. Usually the code that I write runs fast enough that it's, that's not gonna be an impact on the page. But just know that it is possible to do that if you have something that's gonna take a significant amount of time to calculate or say you're, you're looping through something a thousand times and it might take, you know, little while to do, but you have some other stuff that you want to have happen in the meantime. You can write functions that operate asynchronously and you just want to kind of run through and look up the documentation on how to set that up. So, yeah, so now that we're done, we'd say, hey, call, swap dates and then call Set cohort field. Let's run through Set cohort field first because it's super short. All we're doing is saying, hey, set cohort field just means I want to, I want to grab NCD format. Just means next challenge date format. And I want to call my next challenge date. And that was set up here. And so we got that because that was created and we have a default value here. We can see that we've put in March 25th. And that's actually, we can go back. This is when this code was first installed was for the March 24 close date, the March 25 challenge start date. So this code's been in place now since then. So what, five months, six months? Heck, what is that? No, seven months now? Almost seven months has been working pretty well for us. So but you can see that. So we're sending a default value here. That's why we've got var. And it's defined outside of this set of cohort field. So it can be used within this scope. So we're grabbing that next challenge date. We're formatting it to remove the slashes. We just want mmddyy. And then I'm gonna take that string value and I'm gonna set it. So now I'm saying, hey, go find my. Find my element that has data custom dash type as a. As a parameter. That's what we get when we use the square brackets here that says a parameter of my element of data custom type equals ofalatest cohort and then set its value. So VAL says set its value to NCD format. So let's go back and take a quick look at our of a challenge page and let's just. Actually we're going to pull this up and so if we take this code and let's just run this piece of it. So if we take and we run just that piece and we hit enter, then it's gonna return us an element and if we click on this, it's gonna expand down. Cause it. This actually returns us an array of elements. So this call will say, hey, return me all elements that match this. So that's why we get an array here. And we have a zero index. So we only have one of those. But if we had multiples and so we could actually. Let's see if we can actually have multiples here. So if we were to run that same thing and just do. Let's just do a little bit slightly different one just so you kind of know what to expect when you run these. So now we say, hey, give me all inputs on the page. And now you can see we have 113 inputs. So init 113 gives us our count. And this one says just init. It doesn't have a count. So if we expand this down, then you can see we have 0 through 99. And this is just a browser giving us making a little easier to read. But now you can see we have a listing. 0, 1, 2, 3, all the way down we'll have 99. So we've got 99 in this group. Inputs on this page. And then we have 100 through 112. So we have a total of 113 different inputs on the page. So but we just, we just want to grab our custom. Oh, wrong one. Run it one more time. And this just so you know what I'M doing. I'm actually hitting the up and down arrows to actually loop through the calls that I've made recently. And so the browser will actually maintain those. And this works the same. If you ever use the terminal on your computer, whether you use a Mac or Windows, if you do up and down, it'll run through previous commands. So you can quickly run through all these different things that I've run that are kept in my storage. And now I've got one where I've. There we go. So we can just kind of keep going through and then just hit enter and then that runs that command. So if I expand this out and I can go and take a look at this element. So it's giving me the element type input dot and then all the classes that are associated with it. If I had an ID on these, then it gave me the ID as well. So if I click that, then it's gonna actually go into the elements and show me that element. And it's highlighted here so I can see. Okay, great. So I've got. And if I just take a look at this, I can see it's an input type, which I knew. And then it's got data type equals extra and data custom type of latest cohort. And so if you ever go into your pages and in the editor, when you set the custom type. Let's see if I can actually pull up. Click funnels here. So if I go into my funnels, do, do. So if I go in. And let's see if I've got one where I know I've got custom fields on. We'll just pull up any old form here. And so if I just open up in my ed.
Okay, so on the. On the OFA form, then. So you had a hidden field on that page.
Yep.
Somebody's going through to sign up. That hidden field is already populated before basically on page load, it populates the field for the next date and then when they sign up, it gets stored into the database.
Yep, exactly, exactly. So, yeah, so in this case, that, that data custom type is actually one of the fields you set when you create a new input element. So you just create input, it adds it there. And if you go and you pick the type, the input type, it defaults to not set. But if you put it as custom type, and then the, the value you put in here is what, what actually determines that data attribute that we saw of data custom type, that's the name that you set. So this is just a good way. And this is something I use a Lot to grab the actual custom inputs that I have on the page by name and then val, we'll do a couple things. So if we go back and we look at this, let's go back to our page that we're looking at. So we can do a couple things here. So we can input that, and if we do VAL without a value inside and just do close the parentheses, it's gonna grab us the current value. And we can grab that, and we can see it's set to 10, 14, 19, which is the next starting date. The next cohort date starts on the 14th. And then if we set a VAL with a string in it, we can say 10, 15, 19, and we can set that, then it resets that value and just returns us that element. Okay, but now if we again go and just grab the value now we can see our value is now 10, 15, 19. So those are a couple of the ways this VAL function gets called without any parameters inserted. It grabs the value with a parameter in between. It actually sets the value to that that value. So that's what we're doing here is we're actually going through and saying, hey, grab me this element. And when you use the dot val, it actually will operate on the first one. So even though this call to grab this actually returns the first elements, or I'm sorry, returns an array of elements, when you call VAL on it actually it says, okay, just operate this on the first element in that array, set this value. And so that's what we got going on. So if we did so, for example, where we pulled in 113 elements with the type of input, if we just do input val, then it's just gonna return us the value of the first element. So if we do input and dot get zero, which gives us our first one, then you can see the first input that we receive as that 113 element array is actually an input with an ID of submit form action, and its value is redirect URL. And that's the value that we got up here. So that's something to keep in mind with Val. We have a lot of things inside of jQuery that will that operate on every element that's returned. So if we were to say dollar input dot add class, Then that's gonna operate on every one of those inputs. It's gonna call addclass on every single one of them. So we can see now we have my class, my new class on every one of these inputs that, that I run through here. But the.val is one that only operates on the very first element in that list. So if you, if there's more than one, then you need to actually loop through them using dollar each. And so then we would want to say dollar input. Let's see, input each and then from there you can call an anonymous function and say$this.val and we probably, let's say we want to console log their values. So we'd say console log this val. So this, in this instance with the dollar sign, this means the current element being operated on by this function. And it's just a shortcut way of not having to define that. So $this is a. As a special keyword that says operate on the current element that I'm working on here. So we're gonna output this and we want to close out our function and close out our parens. And so now it's gonna go through and show me the value of each input. So if I run that through and it returned 113 times, it returns something that's not right. Oh, because I didn't actually call value correctly, I need to close the parentheses. There we go. Now if I do that, then it's gonna loop through. And now I see all of my outputs. So all my values are being returned in there. So now I have some where they grouped them together. I had five elements that had blank. I had six elements that were false in a row, 12 that were blank in a row, two that were the same value in a row. So that this is just the browser trying to make it friendly for me, but it's doing that. It's actually going through every single element and it's outputting its value. So those are just some things to keep in mind with a.val function, specifically in jQuery that only operates on the very first element returned by this call. And it either sets that or it returns it. Where a lot of your other jQuery functions will actually will operate on every single one that's returned by that call.
With the use of the. So you had the input field there with the custom type. In the past, what I've done is, so let's say I had to calculate a bunch of numbers for a certain customer, somebody who was in the contact database. So I had to calculate a bunch of numbers. And then what I was doing was through JavaScript writing an input field and inserting the number there and then appending it to the body in order to save it into the contact database. Obviously this way is a million times easier.
Yeah, and because the other thing you'll want to remember is that unless you get very specific, unless you add that new input thing, that new input field into a specific location, it has to be part of the CFAR form and has to be named a certain way in order for it to actually get stored onto the contact profile. Then this is an easy way. All that stuff's already done. This field's already wired up to do all that work. So yeah, it's definitely much easier to just use a custom field and then just update its value and then clickfunnels will handle all the rest.
Yeah, no, it's much easier than what I did. But the other way I did it, I don't know if I was using the CFAR form. I may have been. Cause it was an input field, so it was probably putting it into the form and, and I didn't even realize it.
Yeah, yeah, there's, yeah, there's definitely multiple ways to skin that cap. But this, this I found to be much easier. And the other, the other reason that I use this is because especially when you're working on a team is that the rest of my team can easily understand how to set these things and what they mean and that this, this data, this custom type will automatically get appended to the additional info for that contact profile. So then you can use it in things like Zapier and other stuff like that. So yeah, it just makes it handier and it makes it a little more understandable for others, you know.
Yeah, I definitely, I definitely like this because my, my way of looking at it is I want to use built in click funnels elements whenever possible. Yeah, and so now this just saved, you know, three, four lines of JavaScript code.
Yeah, for sure, for sure. Yeah. And that's actually, you know, a good example, like the very initial. So if I were to pull up CF promo code so we can look at the CF promo code and that's what I do here probably kind of. Oh, this is, yeah, this is actually a newer version of that. So let's see. CF dash. Actually let's close this one out. Probably just open this up through CFO Tools. So we'll go in the members area and take a look at this. The CA promo code is one of the very first add ons I created. There's a newer, much better version called Promo Code Pro. But if we look at the free one that's included here and we look at this code, then it's doing what you explained very first, which is it's creating a new field, it's setting the HTML of A field and then it's actually appending that. It's actually inserting that into the page. If we look at, let's see here where it's inserting it. Yeah. So it goes and finds. Finds the last El order product options, which is the order summary. Find the last one and after it insert that field. So it inserts the HTML of that field. But you'll notice it doesn't have. It's just got an input type of text, it's got an ID of promo code and it's got a name of promo code. So this isn't actually going to write to the clickfunnels database. The value that gets inserted here never gets stored anywhere. It's just totally for use on the page. But I had a lot of people that reached out and said, hey, I would love to know what somebody actually entered for their promo code. How can I do that? So that's when I actually go in and out look at the promo code pro version of this. And we pull that up and let me just actually grab this one and insert it into code. Grab that, copy it. I'll put this into my code editor so it's a little easier to read. Now if we take a look at this one, then there's a ton more work going on here because we do all kinds of other checks for expiration dates and affiliate IDs and all kinds of other stuff. Let's see if I can get to the right spot here. But we actually go through and check to see one. Is it enabled? And all that stuff. And then this one actually works on the custom fields. So it's doing the same thing as we have. It's. It's a field that's actually on the page and it finds it and sets its value and does a check based on this. So it's looking at data title, promo input and then. Yeah, so it's looking through this and it's actually. I don't know if we even actually use the custom custom type here, but it is expecting. Yes. So it's looking for. It's setting its data custom type here, promo input name. So it's actually going through and setting the CFAR form. So notice we're creating a field. It's finding it on there because it's looking in the CFAR and is looking for input where the name is equal to the data custom type. And so that means that we're actually going to set its value and we're going to store that on its value. We're actually appending A field onto the CFAR form with that value. That's what this code here does. If they entered a promo field and set its value and then, yeah, we're going to append a name in the promo entered to the actual CFAR form so that it actually gets stored into the contact database and looks like somebody's chatting in here real quick.
So, Dan, I put in the code that I used for it. So I created the variable of total score and then I did just total score append to body and then that saved it into the database for me.
Yep. And you'll notice in that code. So actually, let me pull this up here so everybody. Can you guys see that chat on the screen?
No.
Okay. Okay. So let me actually just grab this out and I'll throw it in my editor so everybody can see it. I wasn't sure if that chat was showing for everybody else or not. So what we can see here, the things that are most important is you set the name to custom Type. And because ClickFunnels actually looks for. They have JavaScript that looks for any input that has a name of custom type. And then it also looks for this data type equals extra. So ClickFunnels has JavaScript code that says, hey, go find me any element, any input that has a data type of extra. And then you also have this data custom type of total score. So these things actually are what tell clickfunnels grab these things and actually add them to the CFAR form. So there's custom code that does that. If you did. If all you did was set a. And so you're actually inputting a hidden field. But if you wanted to insert an actual field that somebody could enter into and you just have this set as input text and you didn't have data custom type or data type equals extra and you set its name to, you know, my field that would never actually get entered into the Click Funnels contact database. That field would show on the page somebody could type a value in, but it would never actually be stored. So you did it right in that having those things. But that's actually just so you know that having the data type equals extra. And the data custom type set here is actually what tells clickfunnel and the name equals custom type. So setting those three attributes. Custom type is always custom underscore type. Data type is always extra. And then the data custom type is what field name you actually want to provide to that. What do you want this to be called when it gets stored in the additional input and then set its value, then those things will always make it through and get stored in the back end of clickfunnels.
Okay. Because. Yeah, what I did is I had to calculate the score, as you can see there. So I had to do a bunch of tabulation and then I couldn't figure out any other way. So I just. Looking at the other fields and click funnels, I basically deduced what I had to do to make it show up. So I should have made that type hidden. It doesn't show anywhere on the screen. Anywhere.
Yeah, no. And yeah, you don't need it to. Yeah, so that's perfect. Yeah, yeah, so that's what that's doing. And so clickfunnels says when somebody submits the form, when they click the submit form button, then go and loop through all the fields on the page that have a name, custom type, have a data type of extra, and grab their custom type value. And then it creates a. It creates an actual field on the CFAR form with that name and it stores it into click funnels contact record. The really cool thing is now if you were to actually go back and want to do an email and say, hey, I want to actually use that total score, then you can actually use the merge tag total score inside of your email.
Yeah, that's exactly what we did. I built this for a client and that's exactly what we did, is we output the score and. And then I also put in there a URL so that based upon whatever their score was, they got a particular URL. So we output their score and the URL to send them to the page that told them what their score meant.
Yep, yep, exactly. And that's what we do with. So when we write email, when we send emails out to redirect them to the appropriate Facebook group, then we actually use this ofa latest cohort and then we send them to, you know, our redirector page. And then we add a URL param of cohort equals and then we output that merge tag. So then the redirector page has got custom JavaScript on it says, hey, look for the cohort URL param, find out what its value is, and then use that to actually look up in the spreadsheet that we have of all the Facebook group links and find the appropriate link and then redirect them to that link. So just another example of how you can take your custom fields and a lot of people don't know that you can actually use those, all those custom fields that might exist on additional info, you can actually use them as merge tags in your emails. So that's. That in and of itself is a pretty cool feature of ClickFunnels that's not really documented anymore. So I'm glad you found that. But yeah, that works really, really well.
So basically I did the equivalent of either a creating my own input field or I did the equivalent of what you did, where you just put in the custom field and then just, you know, stuck a value in it. Yeah, and then I submit it, saves it switch, which, like I said, I like the. I like the way you did it better because that way I can just use a built in click funnels element and I don't have to go through all this brain damage doing the rest.
Yeah, for sure. Yeah. But that's basically what you did, is you created your own on the fly through JavaScript and it worked. So that's great. But yeah, you can definitely do that. And what I usually do is I put a hidden section down at the bottom of the page that includes all my stuff and I usually just call it like C approachables Magic. So I can reference it if I need to, but I just put it all down there and I put it hidden so nobody ever sees it. But the fields are all there and I can then write stuff to it. I can get values out of it, I can use it for kind of temporary storage and all kinds of stuff. And then I can submit it to the backend when I want to. Cool. So, yeah, so let's dump this out and then let's dump this out and then let's jump back into our code. So that was setting that cohort field. So we need that again so we can use on our merge tags we want to be able to send people to the right Facebook group. So we need to know what cohort did they actually sign up for so that I can grab the right Facebook group link later in an email and send that to them or I send them a link that has that coordinate on it so when they click the link, we can redirect them to the appropriate page in Facebook. So that's what that function does. And then the swap dates. The first thing we're doing here is we're setting up some data. So we've got the NC data here. That's the next challenge data. And this is, I'm setting this up for specifically for handlebars to use. So I'm saying, hey, I want to set a key called mmmmn or yeah, mmm, all uppercase. And I want to take my next challenge date. I'M going to format it because this is actually a moment. And so I can call the format function on that and I'm going to form. Get. Give me the named format of mmm and that gives us our three characters month. So I'm sending all those things just because I don't know I've got this set up. You'll notice the same code is set up if you use the, the CF Pro Tools add on called CF datereplace. It uses the newer function of that date Replace Pro, I believe is what it's called, uses this. All of these date replace with math always use also uses all of these. These are just some kind of predefined function. So we could put our curly braces and mmm. And it's gonna return the right thing. So I'm setting these up and then I do that for my next challenge data and then I also do it for my close cart data. So I'm using my variables and I'm calling format and I'm setting that up. I'm just setting up all these different keys in an object so then I can call that later. And so then what I do is I go and I say, hey, now I'm selecting elements on the page. I'm selecting any element that has the next challenge date replace as its data title. And I think we kind of went through data titles and how I use those. Does anybody need a quick refresher on what that data title is and where it comes from?
I know, but somebody else who's watching this might not.
Yeah, I'm familiar with it. Okay, great. So that's our CSS title is where we pull that data title from. You'll see that in almost all of the CFPRO tools code that use that data title or the ones that have some sort of configuration elements on the page that we will work on. Just saying, hey, go through, grab all of those. And that's where I use the each. So grab me a list of these and then for each one of them call this. Call this anonymous function. And that anonymous function is, hey, I'm gonna set the source. This is for handlebars. I'm gonna set the source of this to this HTML, which means grabbing the HTML of the current thing that I'm iterating through and then set my template. And my template is Handlebars Compile. So this is where I'm using the handlebars and I'm gonna just take the HTML basically and I'm gonna pass it into this compiler and then handlebars is Going to do the work to go through that and find all the places where I have the double curly braces and it's going to compile those into replaceable elements. And then I'm going to say, okay, now take the exact same element that I'm operating on. And now I've got this HTML, but now I'm actually passing in the replace data. So this template NC data says grab my template insert, give it this array of data and let it do the replacements on it and then replace everything and then update the HTML and then show that element. In most of these places I have them set to hidden by default in the editor, I set them to hidden so that they don't show on the page until the data has been replaced. And then I do a this show. So you'll see this in a lot of places in CFO Tools code as well, where for like order summaries and product selectors, I recommend setting those to hidden by default so that when somebody lands on the page, they don't see this flash of unstyled content or this flash of hey, there were 15 products in my list and then I went down to five. What happened there? Why was that? I just don't want them to see any products until I'm done doing the work to hide the ones that I don't want them to see. And then I show the entire element and then they'll just see the five that I want them to see. So that's kind of what we're doing here.
I'm sorry, where you have dollar sign source.
Yes.
The dollar sign just indicates to you that that's jquery object.
Yeah.
It doesn't affect whether the dollar sign's there or not. Right?
Yep, exactly. Yeah, sorry about that. It's just. And honestly I've kind of gotten. There are times when I think, yeah, this is a great idea. And there are times when I say, you know what? I really don't. It doesn't really matter to me me. So you'll see it in some places and not others. But yeah, because the dollar this here returns me and in this case it's probably even inaccurate because it's the depth. HTML is actually not returning a jquery object, it's returning me an HTML string. But usually if I just have dollar sign something like if I were to return a dollar sign this, I might set a variable name with a dollar sign in front of the. Again, just so when I'm looking at the code later. Remember? Ah, yeah. This is. This is coming from jQuery. Especially if I have hundreds of lines of code and I've got a variable name at the very bottom, I don't want to have to scroll all the way back at the top and see, okay, what did I actually get here? So it's just a little helpful there. So if you see a dollar sign that doesn't have a parentheses after it or doesn't have a dot after it, then it doesn't have any impact. It has nothing to do with jquery. It's just. It's just a dollar sign. It's just a normal string. But if I say dollar sign and then it comes with a parentheses, that means this dollar sign represents jQuery or dollar sign here represents jQuery dot. So this is calling, setting something up. And this is actually calling the jquery object, calling a function on it. So yeah, so this one here is just a variable name. And yeah, so that's what handlebars does, is it basically compiles. Compiles my. My HTML looks for double curly braces and it looks for anything where it has double curly braces with two D's in the middle. It will replace with the value that we set here. And then I replace that HTML with return HTML from our template call. And so that's how we go through on the page and can find any element that in the editor we put it as double curly braces. Mmm or double curly braces long dash date. And it will replace that value with an actual output value based on our cart close date or based on our next challenge date. And so I do that for next challenge I use the NC data and I basically do the exact same thing for my cart close date. I look for those elements and I use my CC data there, but they're both doing pretty much exactly the same thing. And then this is where I actually work on the actual countdowns on the page. So what I do here is I say, hey, go grab me all the countdowns that have cart close countdown and loop through each one of them. And then I'm grabbing the this. And the reason that I'm grabbing the I this here is because I want to know which one I'm working on. But if I go in and I start this other one that has an each now dollar sign. This that I call right here means that's working inside of this loop. But I need to be able to get to this one because I'm actually gonna set some data on this. So I can't call this this. So that's Why I have this I this here. Does that kind of make sense? I know that's probably not super clear, but maybe if we walk through it'll make sense. I just need a reference up to the bigger elements, the outer element here. Um, so that's why I'm gonna head go ahead and set this one and then this is gonna change each time. So that's why I'm redefining the bar. It's gonna be different every time through this loop. This is gonna get reset and then I wanna use I this in here. So I need to be able to do that. And I'm also going to use it in here inside of this other loop. So I can no longer use $this here because it represents this inner function where I this represents the outer shell of that loop. And so what we're doing here, this is another way to do the selector and it's actually a pretty cool one. You'll notice in this we have a dot is countdown and then we have after that. So we've closed that string. So this is our selector right here. And then we've got comma and then we've got I this. So what that, what that means here is that I'm saying find me all the is countdowns that are within this current element. So it's a way to limit down and filter down where we're searching. Because otherwise if I just said give me all the IS countdowns, it would find all the IS countdowns on the page. But I don't want all the IS countdowns on the page. I only want the IS countdowns within the my current elements that I'm working on in this outer loop. So this gives me a way to filter that down right here in my jquery selector. And so that comes in handy and you'll see that in some places throughout the approachless code. But this is kind of a built in filter right here. And that, that comes in definitely very handy and you can do it in a couple of different ways. So we could say, you know, I'm trying to think about good example off the top of my head. But we can write it as, you know, find me, you know, find me inputs within, you know, form. And so it's a double selector here, right? But it's instead of if we wanted to find all inputs and all forms, then we'd have a comma inside of the quotes. But when you have a comma outside, then these become two separate parameters. This parameter one is your selector definition and parameter two becomes the filter within which you're going to search. And so that's just kind of a built in jquery and you can actually find that if you search on just jquery selectors and Google that and look through it'll show you all the options and how to kind of some good examples on how to filter that down. But that's what we're doing here is we're actually filtering down to find the is countdown and and these. This is a class that gets applied to our countdown element after the JavaScript to create the countdown has run. So the count this data title is our element that contains that. We set that up inside of the editor and then we say hey, after our page is loaded we want to go through each of these, we want to find that countdown elements. And then on that countdown we're going to call the countdown function. This is using that countdown JS library and we're going to set an option. The option we're going to set is the until. That's the date that the, that is the expiration of the countdown. So now we're resetting that expiration. So we have a default expiration set on these. And I think the default expiration is set to like March 25, 2019 or March 24, 2019 when our countdown cart close, the initial cart close happened. But now we're going to reset that until and we're going to go and find our cart close date. We're going to do a difference meaning we're going to find the difference between the current time zone in seconds and so one more time. Yeah, so, so yes, so this is saying hey car close date. And so this is our moment. So this here, this call is getting our current moment in New York time zone and then it's gonna compare it to our cart coat cart close date which is also a moment and it's gonna find the difference in seconds and then, and then it's gonna show that and so that resets our counter for the countdown. And this is basically how many seconds should be left in that expiration. So that's what until means is it can take in several different things, but what it can take in is the number of seconds until it should finish counting down. So it's like this is saying, hey, grab me the current moment in New York time and the cart close and give me the difference in seconds. So if the cart close date is in the future, then I'm going to get a number of seconds until that date and then set the countdown timer to count until that number of seconds has been reached. Does that kind of make sense with that explanation?
Yeah, you kind of lost me on that. Do you have more than one countdown timer on the page for.
Yes, we do. We actually have.
Okay, so you have one for every potential date out in the future.
No, we do not. We have multiple countdowns that are all counting down to the same time. So there's a countdown at the top of the page, there's a countdown at the stack, there's a countdown at the bottom of the page. They're all counting down to the same time.
Okay, so this line here that you have highlighted with the seconds, that's calculating and then setting the time inside of the timer element.
Yes, yep, absolutely. So it's resetting. So when we create the timer in the. In the clickfunnels. So and we can see on the page here if we load this, that we've got this countdown timer here, right? And it's counting down. It's one day, six hours 59 minutes. If we were to actually look at and inspect this wrapper element. So let's go and look at this. So the way that these by work out of the box is we've got some data params that are set on these things, right? And because it's a continuously changing element, it's not gonna give it to me. So I'm gonna have to drill down to it a little bit. Just a second. We'll actually find it. And so, so we're adding this el countdown element to the page. And then we can see here that the day to date on this count wide countdown element is actually set to August 4, 2019. But that's not what it's counting down to, obviously. It's counting down to one day from now, which is, you know, October 13th. And the date of time is 2100, which is 9 o' clock New York time. So that's how it defines initially what when should I count down to? But what we've done in this code is we've gone and said, hey, change the option of until, which is what this is usually being calculated on is this is setting the until when the page initially loads. Clickfunnels will take this data and set the until August 4th at 9pm New York time. And we're resetting that option on this particular countdown because we have multiples of these on the page that all have the data title cart close countdown. We're gonna go through every one of those and do the same thing, which is to find its countdown elements and the. And the Countdown element is actually wrapped in this. So this is the. In fact, it's even wrapped. Actually, this one has the is countdown. So notice now that we have this dash is dash countdown. So this is the countdown element now. And you'll notice it keeps flipping in purple, keeps pulsing in purple. That's because the JavaScript every second that this thing changes this, when it highlights in purple, that means that element is changing. And so every second it's changing the this element because the spans inside here are changing. And you'll notice they'll keep. It'll keep going away because the spans get removed and then added back and removed and added back. So this thing's changing every second. So. But it starts out Counting down to August 4, 2019 at 9:00pm New York, 9:00pm Eastern Eastern Time. And so we're basically going in and saying, okay, grab the countdown, reset the option of the until to a number of seconds between the time I'm closing the cart and the current time in Eastern time. And so if, if that's already expired, if we're already past the cart close date, then this, the countdown will be expired immediately. It'll come back with a negative number of seconds, and the cart and the countdown will be expired. But in this case, and what we'll always see is that the difference, the difference between the current time in eastern time and the cart close date is a positive number of seconds. There's, you know, there's, you know, 2,000 seconds or 2,342 seconds until we reach the cart close date. So go ahead and reset the until option of the countdown to that number of seconds.
Okay, so you're resetting the timer itself every second. I thought, well, okay, let me put. What I thought you were doing was setting the date so, you know, when you go into the field itself to set it up, you put in the end date. I thought what you were doing was setting that end date and then letting clickfunnels calculate the rest on the fly.
Yeah, no, we do not. Because that's already calculated by the time the page loads. So we need to reset it because Click Funnels code is gonna set that initially. I need to reset it after it's done. But we only do that one time when the page loads. So the clickfunnels version of setting that timer, setting the until. Cause clickfunnels is doing this exact same thing, right? They're setting the until on that when the page loads initially. And then we've got additional code that's gonna override that after that's happened when the page loads.
So when you do so you do your like your document load. What if you reset that again on window load? So the sec basically the second time it loads is the way I look at it, if you set that date inside of the. Basically where you set it up in the element itself. So that wouldn't work either. So you do have to set it like you did it.
Yeah, because yeah, clickfunnels is already fired at that point and they don't fire theirs again. So the clickfunnels code to actually set the countdowns has already fired. Where they look at the actual date parameters, which is how they do it. They look at this data date, they look at that and they're gonna call that one time. Hey, when a page loads, look for data date, look for data time, look for data TZ and go ahead and set the until option on this countdown. That only happens one time. Their code fires before our code does. So we can't then afterwards go and reset this data date to something else and have that take any effect. I could go in and change this here manually and say I want this to be 1014. That's not gonna change anything because their code to set the countdown seconds has already happened. So that's why we have to go and actually reset the until. Okay, this only happens one time. It's not happening every second. It only happens one time. It's just our code. We know they've already fired their code. We need to do the same thing, but we need to fire it our way, which our way is going to be a different calculation than theirs. And the reason being is that I don't have to go into the editor every time we change a course. We never have to touch this page in the editor. It's set. We just set a default date and then it stays the same forever. Doesn't matter what it is. We're going to pull this value, this cart close date is coming from our spreadsheet. And so once we grab the spreadsheet now we actually want to reset all of our timers to based on that cart close date from the spreadsheet.
Okay, thanks.
Does that help? Yeah, yeah. Okay, cool, cool. So that's kind of the big one for this. We actually go through and this is the biggest thing for the spreadsheets. This is how they work. The whole point of these is to replace text on the page and we can kind of see where that text is if we take a look at this. So we've got a few places where we're replacing that. And if we reload this page, you'll notice that some of the stuff doesn't show when the page first. First gets drawn. So these things are gone, right? No headlines here, no countdown, nothing like that. And now they're here once we actually do that work. So that's the this show that happens in these places. And that's the. That's the this show here. So these things get hidden until the replacements get made.
So.
And then once they get made, we can see here we've got October 14th and here we've got October 13th at 9pm Eastern. And then we've got a countdown that's replaced here. If we go and look at the code and look at the actual view source and we'll do a find on this for double curly braces. Let's see where we've got one found. So we can see. Let's see if I can actually blow this up. So we can see here that still. Still blowing up. Let's see if I can find it again. Sorry, it's being really laggy. Oh, goodness. That's why it's being so laggy. Blew it up way too big. I've got about three or four seconds between every button click here to actually increase this. Okay, so now let's do a fine on that. So now we can see this line of you have 30 days starting at mmm do. So the code is actually going to go through and find those curly brace references and replace them. So we can go back and look at our code and see the actual text here for mmm is January or February, March. And then do is the actual first second. And so that is refining. That's probably later down the page, but you can see that that's like October 13th. That's mmm, do. And then we have at. And then we have a time zone, we have an actual time. So that's our current close time. So. And so if we were to look for that, that's probably going to be. I think we. I think this might actually be static because I think we say it's always going to be at 9pm Eastern time. So I think we're just pulling in the date here. But that's kind of what's happening on these is it's actually replacing those things and then it's showing the entire element. And then the other piece was to actually reset all the countdown timers to count to a certain date. So that is our spreadsheet code and kind of how that works.
Okay, now my only question on that is so you defined all these different ways of displaying the date, but you only used some. Some of them. So you would really technically only have to define the ones that you're going to use.
Yeah, absolutely. Yeah, absolutely. I just defined a bunch of these because I already had them set up and I wasn't sure how we might want to display that date. So I just said, you know what, we're going to go ahead and calculate it out, we're going to set it, and then if we never use them, that's okay. But yeah, absolutely. We would only have to really define a few of those. We really only use. Mmm. And do. I don't think we. And I think we used the year in a couple places, so we'd only have to have maybe a handful of these. But I already had code to pull them in, so I just copied and pasted and reused it.
Okay, so now with the handlebars, you do the handlebar compile. It basically just wherever the handlebars were, it just grabs that because you had the format in there. So then it just grabs the date and sticks it in based upon what the date is.
Yeah, exactly. Yep. So basically what. What we're doing here is we're setting up an object that has a key called YYYY that has a text value of the formatted date, the year. So it's going to be 2019. So what's going. Getting stored there is 2019 as a.
String and then format that comes from moment.
Yes, exactly. Yeah. So this is a moment object. And then we're calling the format function on moments, which is returning just. This is long date format, or it's returning just the day number.
Okay, and where. Compile. That's a handlebar function.
Yep. Handlebars.compile is just. Is called from handlebars. And it's saying, okay, I'm getting data input. So I'm getting the input of my NC data here. So that defines all of my keys and values. And so I just have to go through each of my. Each of the places where I find the double curly braces. And within those is going to be a name which represents my key. And then. So just grab the key that. Grab the value for that key and replace the text. And that's all it's doing.
Okay. And then you have template below that. Where. Is that a standard JavaScript or jQuery? I mean.
Yeah, no, this is actually this. This here. So handlebars.
Oh, I'm sorry, that's a variable. Okay, I'm sorry.
Handlebars.com pile actually returns a function. So template in this case is a is actually set to the value of a function. And then template here is the actual calling of that function. Does that kind of make sense? Because we could also do var my function equals function do some stuff. And that's not actually gonna do some stuff here. That's just setting my function equal to a function called where I do some stuff. But then later I can call myfunction and now it will actually do the stuff. So that's kinda what we have here. Template is now set to a function and down here we're actually calling that function. It just happens to be named template, but that's all provided by handlebars.
Okay, then NC data, that was the.
That's my next challenge data variable.
Okay.
Yep. And then CC data down here is my. Is my close cart data variable. So that's all this stuff defined.
Okay. Yeah, all the date formats. Okay.
Yeah, Yep, yep. And yeah, in a good example of how this works again would be the CFDate Replace Pro or the CF Daterplace Pro with math will go through these same things and they both use moment and they both use handlebars to go through and actually do the replacements and give you the ability to insert, you know, whether it's a copyright date in the footer or whether it's, you know, doing things like this. And this is pretty much just a rewrite of the Date Replace Pro. In this case, I rewrote it just because I want to do two different sets of data. I want to do a next Challenge date and a car close date. Otherwise it's just working on the current date. But in this case, those Date Replace and Date Replace Pro work on the current date. And then these actually work on a date sometime in the future.
So.
So with the code and the way that works is pretty similar in those. So if you ever need, you know, a reference, you can definitely look at those as well. So yeah, so we're everybody doing okay time wise? We're about on average two hours in here.
I'm good.
And we still have a ton more to cover, so we'll kind of keep moving through this code. So the next one, the next thing down is the order summary. And I think Dan, you had mentioned this one and kind of being the order summary and how we totaled things up. Did you have specific questions about how this works? We kind of go down and see this on.
No, I mean, not necessarily the. I mean, like I said, I was just kind of looking at, looking at the whole thing and it was all jumbled up with everything else. And I just said, well, you were looking for examples for Saturday, so I thought, hey, why not go through this one?
Sure, sure, yeah. So this one is pretty intense actually of what it actually does. But the end result is just this down at the bottom of this page, which is listing out. Because normally what you'd see in clickfunnels is just item amount and then you see a single line item for the product and it's price. And if you add the bump, then you would see, you know, the, the single product and the price for the bump. So you'd have the bump product and then the core product. That's vanilla Click Funnels. They only allow you to sell one thing at a time and maybe a bump. But in the case of a bunch of the custom code we do, we may actually have multiple things. So in the case of, hey, I want to actually select. Right now we have our digital only selected, but if we want to do the physical, then we actually are going to add a whole other product to this. We actually are going to add our challenge product, we're going to add our shipping and we're going to add our bumps. Now we have three products added. Vanilla. Vanilla ClickFunnels won't allow you to add more than two products. Your core and a bump in this case.
Well, where I really lost it is then you actually have one more which is international shipping.
Yes. And that's a whole nother add on. So that's our CF International shipping add on. So order summary is just this piece where it actually adds the order total and it allows this order summary to be rewritten with more than two products. And so really it's doing this total enough. And then, yeah, if we change and do change our country here to say United Kingdom, then now we'll have shipping is 29.95. And if we change that to United States. Sorry if you guys are hearing that. Ding. My wife, I think is texting me that the Amazing Liz Benny is staying with us as well and I think they're going out to grab some breakfast. So now we notice that our shipping has changed from $29.95 to $19.95. And so that's the CF International shipping add on. So it's basically looking for any changes in this country field and it's swapping which products are attached automatically to the order based on that. So we'll jump into that code as well. So I'm gonna run quickly through the order summary. We probably won't dig into every bit like we did with the spreadsheets, because the spreadsheets really something that is kind of new and I don't even have an add on that does that. I just. We've used it in several places. I'll probably use. I'll probably roll that out as an add on in the future. But it's kind of a cool piece and it's got a lot of kind of cool things. So I want to make sure we made it through that one. The order summary is basically going through and we'll pull this code up here. That's our next chunk of code here. And it starts with this script and we start with some configuration at the top. And this is one of the first ones where I started actually setting separately configuration options so these things can get changed. And that's why I have hey, this section must be included. But you don't always have to change it. It's optional configuration. And you can modify the configuration with Your own product IDs and your own prices. And in this case we do. We have the product ID 1748891 and we have a value, an actual price set of zero there. And I'm not sure why we have that set as zero. I'd have to go back and it's been a while since I can figure this. But this one, this 8893, that's our, that's our shipping price for the domestic shipping. We set that as 1995 and you.
Probably set it to zero because if you don't order the. The box and the books and all that stuff, then zero.
Yeah. Actually I do have this one set because we actually do have a product that as a. Is a free shipping that we do for special events. So there is the potential to buy that.
So you can pay just, just a hundred bucks and not get the box of stuff. Yeah, so that might be what that is.
Yep, yep. And so, so yeah, and then we've got. Okay, so we've got product prices. We can configure this. You can have 50 different things in here. And really this comes in handy when. Super handy when the add on is if you've got a price that shows in your price display override. If you're. You know. And we don't actually have a visible product selector on this page, but if you were to set. And let me do something real quick is my doc is showing up over the top of some stuff and I always hate that should be down on my other screen. That's not it's going to take forever to pop up here because. There we go. Finally, let's get rid of this doc. Okay, now we can see the whole page. So what if we had a product selector that was visible on this page and actually let's do that. We've replaced our product selector with the visual version of that with the. Just the up at the top. We've got the. Do you want the physical or do you want the digital? But if we were to look at and pull up and I do this quite a bit. There's a few ways to do this. Data type equals ORPO is our product selector. That is a quick and easy way to find the product selector because they always have a data type of orpo unless you're using a two step order form and then it doesn't actually get a datade type at all. And you just have to look for the first element that has a class of EL product options. And you might notice if you were to look in this code that I actually reset that so that I can actually find it easier later. Actually this one I look for the. I do this with the CF order summary. I look for the first. The first EL order product options that doesn't have an input or select and I set a data title on it so I can find it easier. You could also set that data title manually in the editor, but you can't do that on a two step order form. So I have to. I have to reset this so that way I don't have to. That way I don't have to have different code for my two steps than I have for my regular one step order forms where you're manually inserting an order summary. But you can do the same thing with the product selector as well if you ever need to. But if we actually show this. So I just found the order order selector element on the page and I set it to show. And we should see it up at the top somewhere if I actually go and find it. Now let's click and inspect. Might actually be in a hidden row. So there it is. Let's see. We can actually should be able to scroll into view. It's already in view, so I probably have it wrapped in an element that is not shown somewhere. Row, outline, display, none body. Sorry, I'm just trying to find where the element is even at. Okay, well we'll probably spend too much time doing this. There actually is a selector on the page, a product selector on the page. It's part of this row. There's still a CSS with a display none important showing there in your inspector. Ah, yes, that's why. There we go. I turn that off. There we go. There's my product selector. Thank you for that. So, yeah, so there's my product selector. So now we'll see that we've got different products. The way that the order summary works is it first guesses based on this display price that we have. So if this display price was something, and I see people do this a lot where they, especially with subscription products, where they have three payments of $37. Well, the only way we have to actually grab the price, and I haven't rewritten the code to actually use subscription products properly, we can actually inspect this element, take a look and we'll see that clickfunnels actually does set an amount. So it sets a data product payment type of one time and then a data product amount. So we can grab the price from here. But for subscription products, that's always been zero because a lot of times you're actually setting as we don't know what the price is. Stripe knows what the price is and what to charge. So the subscription products won't work, right? They won't have a price. So the only way we have is actually grab this display price and strip out all the text except for the numbers and assume that that's the leftover price. But what we have in the order summary is what happens is if it says three payments of 37, it will return a price of $337. So that's why in the code I have this ability to configure the actual price. So there you could set a price of $37 for that first payment, or if it was a free trial, then, you know, and it could be 39.97 after 30 days. Well, we wouldn't want it to be, you know, three, you know, whatever, $39,907 or whatever. So we want to be able to set an actual fixed price for that. So that's this configuration here. And then for free products, I want to be able people to actually be able to change. What does it say in the order summary? Does it say free? Does it say something different? And then also for the order total label, where down below we have the order total label. So that's this by default, it's order total. But we could change that to say something like due today. And I usually do that when I have like three trials or trials that are maybe $1 today, but $37 after a month. I'll say due today is a dollar. And so I'll say due to due today on that. So that just gives people some ability to configure this stuff without ever having to worry about all this other code or finding it within this code to see what they want to change. And you'll notice that what we've done here as I set a variable called cfpt, this is a global variable, it's a window scoped variable, and it's set to an object. And the reason I have to set this up first, I set this up as an object first, and then on that object I can set a key of prod prices and I input several different keys there. And then I can set another key of free price display and then another key of order total label. And I set all those things up on this CFPT for CF Pro Tools. And then within here I actually reference those things. So now I actually grab and I say, hey, my internal version of CF Pro Tools variable is going to be window cfpt. Or if that doesn't exist, then just set up a default object so my code doesn't blow up. And then I'm gonna grab my CFPT prodprices, or I'm gonna grab another empty variable, another empty object, again, just so my code doesn't blow up. So I'm kind of setting some default values. So for this, this is a shortcut way of saying, hey, set it to this if it exists, and if it doesn't, then set it to this other default value. And so I do that several times through here so that I can get my product prices, my free price display, and my order total label. And then basically I'm going to shortcut through a bunch of this. But you can see here, I end up finding my CF order summary or my element that has a data title of CF order summary. And I hide that element temporarily while I do a bunch of work. And then I'm going to go through, and I'm looping through. Let me scroll down a little bit. First thing I do as I grab my template of what does a row in the order summary look like? And so I grab that template and I find that element and then I append to it some additional stuff here. I give it a width, I add some classes, and I'm setting up a div, a new div here. Right, And I'm setting this up. Sorry, I'm gonna, I'm gonna check this dinging real quick. Oh, different. Different ding. Okay, so we're good there. And I'm actually going to Set my notifications off so they quit dinging on everybody. Oh, okay, there we go. So anyways, I go through and basically I end up going through here and I find. Give me my summary template, give me a row within that. Set some classes up so I can define some things, some custom styles on it. I set a width to inherit, override some things. I set up a default order total of zero, meaning that, hey, start at zero. Set the currency to US Dollars by default. We look for that later when we go through our summaries. And then set up an empty number of carts that are in our products that are in my cart. So I have an empty set of cart products. And then the next thing I do is I go through every input on my CFAR form that has a name of purchase product IDs, and it's actually checked. Those are the products that are currently on the order. So if we were to look at. Go back and look at our elements here, and we go through and scroll down. Let's just do a find here. Cfar. Okay, so here's my CFAR form. And if I scroll down here, let's get this up a little bit. Got my CFAR form. And now I've got all these elements, right? And these spans where it says DF data cf product template. These are my actual products. So I get two fields here. One's One's kind of a legacy. We've got a radio button. We've got a checkbox. The radio button is kind of a legacy. It's just Purchase product id. The ones we're concerned with are the checkboxes named purchase product IDs, then square brackets. That means we have an array of these, or they can be passed to clickfunnels in the back end as an array. And then we have a data attribute of checked equals checked. That means that this item is checked on this form. And that means that clickfunnels are gonna see that as a product that is on this current purchase.
Can you make that form visible on the page?
Yeah, sure can. Give me just a sec. So if we do. So now should be showing on the page, if I did it right. And if we overrode our css. Yeah. So now you can see there's a couple of checkboxes here. Yep. And so we don't see anything else to go with them. But those are those checkboxes. So if we actually inspect those, then it should take us to those checkboxes that are checked. And so we have our $100 product there. That's our core. And then we should have our shipping product. Yeah, so we've got our shipping product that is checked. And so this code for our order summary is just basically looping through each of those ones that are checked. And then it grabs its product ID by saying, hey, this, what's its value? Because if we look at, if we go back and look at this input, it has a value of right here, 1-747-886. That's the product ID. So we can go back and look in our code and say, okay, so grab its product id. And then if cartprods, that's this object has own property, means find a key of product id, which means, okay, we've been through this before and we already created a key called product id. So if already has one, then just find the quantity within that key and update it, add one to it. So quantity plus plus means increment by one. Otherwise if we can't find it, then go ahead and add one. So go to cart prods. Set a prod ID key and equal it to an object that contains an attribute of quantity or property of quantity and set it to one. So that means add one to my cart. If I've already got it in my cart, then add one to the one to the value that's already in the cart. So this could start out at one and we add one to it. So now we have two. Or it could start at seven and let's add one to it. And now we have eight. So that's how we define. So this is how this code figures out. How many items do I have in my cart? And let's set some quantities. And then what we do down below is go through each one of those cart prods. In this dollar dollar each is a jQuery function built in. So this allows you a way to loop through arrays. So this cart prods or objects. This cart prods is an object that we can loop through that has a ton of keys on it. And then we figure those out. And then within there we're going to say, hey, so it's going to call an internal function, an anonymous function, and it's gonna pass in an index, which means the current, the index of the current item and the value of the current item. So in a normal array that start with index of 0, 1, 2, 3 on down, and then index 0 could have a value of a string, it could have a value of a number. So it's gonna loop through those things and we know that we have that value, which means our current Cart, price, product. We set a quantity property on that one. So we're gonna say give me the current value and give me its quantity. So that could be one, could be eight. And we're gonna set that into a variable within this loop called quantity qty. And then we're gonna grab its product id, which means just grabbing the index of the current, the current item. So this index here, and that's my product id, because that is what we set up above. We set this car prods, we set an index there and then loop through that. And now we're gonna do some work to duplicate our, our summary rows based on our template. So that's another thing that we can do. And if we look through those, then we'll notice, hey, we got our summary template up above, which means just it grabbed me the first row and strip some stuff out of it. So I don't start with the default values, but now I wanna take that and I wanna clone it, which means I'm gonna create a duplicate of that that's not actually in the HTML yet, but I want to do some work on it. And then I'm gonna append it to that. So grab my summary template and clone it. Don't, don't modify the original one. Just give me an exact duplicate of it. And then I'm gonna set that to another variable. And then I'm gonna take that variable and I'm gonna start setting different, different data attributes on it like quantity and product id. And then I'm gonna, in this instance here, just, just so you know, this was kind of my shortcut way of figuring out, does this person have cart mode enabled? If they do, then I need to look for quantities. If they don't, then I don't need to worry about adding a quantity column to this because they're just using the default ClickFunnels, which is one of anything. So order summary can be used with or without CF Cart mode. And cart mode just allows you to have different, multiple quantities of the same product on the page.
So just to be clear here on the quantity as it's calculating it initially. So you, you got the hundred dollars for the book, and then you got $19.95 for shipping, and then you get the order bump. Would that give you a quantity of three?
No, that's going to give you a quantity of one of each of those keys. Okay, so we'll have a. We'll have a. We'll have.
This is essentially only if you have cart mode running with it and somebody wants three t Shirts or something.
Yep, exactly. So that's the only time that you would get a quantity different than one is if you're using cart mode. But this I want the order summary add on to work for either case. So vanilla click Funnels, you're always going to have a quantity of one of every product, but with cart mode you could have a quantity of 10 of any particular product. So. But I don't want to, I don't want to output the quantity heading if they're not using cart mode because we know it's going to be one of everything. So this is just a quick check. Hey, if I found any elements with a quantity select class on them, which is what I use for cfcart mode, it's a drop down and it has a class of qty select if the length of that selector, meaning I found more than 1, if the length of that selector is greater than 0, then go ahead and add to the product name column, which is the heading, add a span that outputs to this row, add the quantity there, which could be in most cases would be one if they're not using cart mode, but if they're using cart mode it could be 10 and then add the product description. Otherwise the product name is just equal to the product description. So we're just saying, hey, set the product name equal product description, which is what we see on the page when we look at this. Scroll back up here and find a little bit. So this is the product description. So this code is just basically saying, hey, set the product name that I'm going to output in my summary equal to that product description. But if I have the cart mode enabled, then go ahead and add the quantity to the front of that and then add the product description. That way it works for either or. And then it kind of loops through and says, hey, you know, go through, find the product name, set it to HTML the product name. If I have a free price display and my subtotal is greater than is equal to zero, then set my free price display, which is, which is basically setting my price, then set this price over here to whatever I defined as my configured free price display. Else just output it as whatever the price display is. So that means, hey, if it's not a free price display and its subtotal is not equal to zero, then go ahead and output my price, whatever my price display should be, and then loop through that and then take that current template and append it to. And then my append to is a variable up here and up above I define how do I find my order summary and that's what my append to is set to. So I'm going to append it to my order summary and then update my order total to add in my subtotal. So each of these I went through and actually calculated the price, right? So I have subtotal here equal to my quantity times my product price. And so I add that subtotal to my total order total. And then down here I say, hey, if I have an order total, which means I've got a product on my purchase here, I've got products in my cart, I have value, then go ahead and take my total template and or I'm actually taking my summary template again, I'm cloning it and this is going to be my total template because I want to output this row down at the bottom and I'm going to take and I'm going to add the CF order total row so I can give it a border, so I can give it some style. And I'm going to go ahead and grab and I'm going to format my order total to a currency string and then I'm going to go ahead and append that again to my append too. So it's again going to be my last row. This happens last. And then I'm going to go ahead and show my order summary and I'm going to set a window variable called CFPT order total to that order total value. And the reason I do that is I actually have the Facebook total tracking code that uses this, this window variable to fire a Facebook pixel. So I want to actually just set that on a window scope variable so I can use it from other add ons. And then otherwise if I don't have an order total, then just, just hide the order summary and set the Windows CFPT order total to null so that my Facebook pixel knows that I don't have value and it shouldn't fire anything. So that's kind of the order summary in a nutshell. And I know I went through that one super quick, but it's, it's pretty deep and it probably take us several hours to go through and kind of do a line by line dissection of that one. But basically all it's doing is you think it's taking the existing order summary, it's making a clone of the first row, so we kind of have a structure already there and I don't have to redefine it manually, so it's kind of using what's already built there and then it loops through and it says, hey, how many products do I have that are actually checked on my CFAR form? And loop through them, grab and update a quantity. If we already have it defined and there's more than one, if we don't just add one to the cart and then find the price, and then loop through each of my cart products, find their price, add that to a subtotal, and then at the end, add that subtotal to an actual order total. And then once I've done that, loop through all my products, then see, hey, did I actually have products on my order? If I do, then go ahead and let's add a total to the line, give it an output, the total price.
So basically what you're doing is you're taking the order summary out of the box, you're putting that element into the page and you're just cloning the first line and essentially duplicating it. And I mean, basically changing out all the text.
Yep, exactly.
Yep, the element. But you replaced everything inside of it.
Yep, Absolutely. That's exactly it. Yep. That way if they go back and change the structure of the order summary element, I don't have to worry about. Oh, man. Crap. I used to write this in the code manually and now they change classes on me. I've got to go back and update my code. Nope, I'm just going to use what they have out of the box. I'm just going to replace the pieces of text within that. That way it's a little more transportable into the future.
Okay, cool.
Yep. And so that's order summary. And then we've got some custom styles here that are included in that to set, you know, like the order total row is set to bold. And we've set some margin. If there's a quantity head, we give it a width of 3em. So it's, you know, kind of gives a little bit of a column. Look again. Product quantity. We do that same thing. We set its width, we set it displayed inline block. We do some custom CSS stuff. And then we've got our three step order form on the ofa. So this is just some code basically that says, hey, look for the click of a button. So this is code you might use quite a bit. Is, is this is saying, hey, on any element that you find that has a data title that contains step one button, on the click of that button on the click of that element. So that's what this dot on. And so basically the dot on is a function and it's just looking to see, okay, what Type of event are you looking me to. Looking for me to fire on? So we're saying on click.
What's the difference? I'm sorry, what's the difference between on click and just going click function?
The dot click is just a shortcut to the on click.
Okay.
The thing that you can't do with a dot click function that you can do with on is you can actually call multiple events. So we could say on click or on mouseover do this. So it's going to call the exact same thing. These are both independent events. You just put a space in between and you could do, you know, mouse out. You could do, you know, all kinds of different events. You could do actually hover. I don't think is one that's a pseudo event. But yeah, so there's so on is is actually what's being called. I. I don't always do that, but sometimes I have a preference for. I prefer to call on because if it's actually what's getting called anyways, it's not bypassing the dot click in itself, is calling on with the click event. So it's doing the exact same thing. So why not just call it directly? So yeah, so that's kind of what's happening here is we're saying, hey, when somebody clicks on a button that has a data title of step one button, then call this function and that function is just gonna say, hey, find step row, step two row and show it by default, step two row is set to hidden in the editor. And then this is just showing that row and then it scroll to top. It's calling the function scroll to top, which I have defined down here. And it's passing in the selector for where I want to scroll. And then the return false is set here because we don't actually want that button. That button has a URL of hashtag. Well, if you just call hashtag, hashtag is just gonna scroll to the top of the page. And we don't want that to happen. So when I do return false, then that means just ignore the fact that that button even got clicked. So it's gonna fire this code first and then it's gonna return false, which means just ignore it. It didn't mean to do that. So that's why we return false here so that it actually cancels out the click that actually happened. And so the scroll to top function, we'll just cover that real quick. So this is a handy thing, and this is really the handy piece. You can call this in and of itself. I just was Gonna call it several times. So I wanted to make up again kinda we talked about last week, which is a. Don't repeat yourself. If you've got something that you're gonna call multiple times, write it as a named function that you can then just call instead of having to re, copy and paste the code. That way, if you need to modify something, you have one place to modify it. So that's what we've got going here. We've got a scroll to top function where we just pass in a selector and the selector is this. And then in that, we're gonna say hey, on the HTML body animate. Which means animate is just gonna give us the ability to pass in a time, the amount of time we actually want to animate. And then animate takes in these properties of, okay, what is it you want me to animate? Scroll top is one of those animations. So basically that means, hey, slowly scroll to the top, take 500 milliseconds to scroll to the top, and we're going to set Scroll Top means okay, well, where do you want me to stop scrolling at? So that's where we say, okay, take our selector, find the first element that has that matches that selector, and then find its offset, and then give me the top value of that offset. And then that, that will return an actual pixel number, which is going to be different depending on the browser and the layout and the font size and all the default things. So we need to, we need to be able to find that, find that number. We can't just say, hey, scroll to 1134 pixels. That might work for us, but it may not work for everybody. Depending on how wide their browser is, the text is going to flow differently. So we just need to say, hey, for the current browser, find me the top of step two, grow is what we're doing, and then scroll to that over 500 milliseconds. So this function I've used a lot, it helps to do things. If you say you have an error message you want to display, but nobody's going to see it because it's above the button they actually clicked. You might want to scroll to that error message so they actually see it. So you can say, when they click this button, if an error happens, scroll to the error message. So you could use code very similar to this. In that case, you might, you might not use CFP to selector. You might, it might be a fixed selector. So you might say dot error message. So it's gonna say, oh, yep, go find me my error message. Find the first one. And if you only have one, you don't even have to do that. You could just do dot offset. But dot first is just a way to get me the first element of that array and find its offset and then find the top property of that offset. So offset actually is gonna return not just a single number, it returns a structure of numbers. So we want to grab the top of that offset and that's pretty much the. And then the step two button does the exact same thing. It just does it on step three. So that's, that's basically the way the three step works. It's just taking and duplicating. It's doing very similar thing to what a two step order form does. It's hiding and showing elements on the page. It's just doing it in a way that we attach the buttons that we can actually build ourselves. And so that's a three step order form. That's the entire add on. It's pretty simple. Then the next piece is this one won't go through because it's kind of very specific to the ofa. But it has to do with actually having different products that we can make available based on URL parameters and things like that. Then we have promo codes and this is the Promo Code Pro add on. So we've got configuration there, you've got promo codes. You can use different product IDs you can set up and what to select afterwards. And then it goes through all the promo code, promo code add on. And I won't run through Promo code Pro again just for the sake of time. All it's doing is swapping out what products are visible on the page. And so in this case none of the products are visible. But we're making it so that we can change which ones actually selected if somebody will enter a promo code. But right now there's nowhere for anybody to enter that because it's not enabled. But just so you know, how the promo code product add on works is it actually just says, hey, create two versions of the same product. One has a higher price, one has a lower price by default, hide the lower price one. And then if somebody enters the appropriate promo code into an input field, then swap and hide the higher price one, show the lower price one and select the lower price one so that now they've got that discounted product selected. So it's not actually taking and changing the price of anything or do anything like that. And clickfunnels, it's just high. Hiding and showing products based on should we show them a discount or should we not show them discount.
The script above that that you went through that you just scrolled through real fast. Basically the upshot of it was that you pass a parameter in with the URL and then based on that it shows certain products that normally we wouldn't see.
Well, it actually does a few things. Yeah. So it actually we've got this only on flag that's set here that we check to see up above. We set this only on flag equal to true. So it's set the true to only show the input field when the URL parameter contains pcon equals 1. So that's built in to say, hey, if the URL parameter doesn't have peak on equals 1, which means PC on for promo code on that don't even show the promo code field. And so that's what this checking for here. So that's really the URL parameter that's most key is do I want to show them the promo code field or do I not want to show the promo code field? And the reason for that is you should never. It's always what I say with promo code add on is you should never show a promo code field on a page unless you know the customer that's landing on that page has the promo code or has a promo code because otherwise they're going to see, oh man, I could get a cheaper price, but I don't have a code. Let me go spend some time and find a code. And then they go away and never come back. So always make sure if you're going to use the ability enter promo code field. Make sure you've been on a webinar where you've given them the promo code or they've gotten it from an ad somewhere, or they've gotten it in the email somewhere. And you just want to give them feeling that they're getting a special, special deal that nobody else gets. But in order to get that deal, they have to enter a special code that actually will can really increase conversion rates, but it can really decrease conversion rates if they don't actually have a promo code. So this is just my way of saying, hey, if I want to set this order form up so that it works for everybody, whether they're coming straight to, you know, they're just doing a Google search and they find my order form, it works for them. They don't see the promo code then. But for those people that are on my email list that I want to send them a special deal, I can just add the parameter pcon equals 1 to the URL and then they'll see the promo code field and they can enter it. So yeah, so that's where we call the built in function geturl parameter. So this is part of clickfunnels. This geturl parameter function is defined at the window scope by clickfunnels. So I just use it. So I look on the URL for a parameter called pcon and then I've and I set my promo flag to that. So then I say, hey, if only on flag is equal to false, then my promo enabled is true, which means I want to show my field. Else if promo flag meaning this promo flag PC on is set to 1 and promo flag is not equal to null, meaning okay, so I have a variable, a promo flag here. That value is set to something and it's not equal to the string null, which is the default value. If git URL parameter doesn't find it and my promo flag is not equal to zero, then go ahead and set my promo enabled to true, meaning show my field. Otherwise the default for promo enabled is false, which is what I set right here, which means don't show my promo code field at all. And down below we can see that I actually go through and I look for that promo enabled. So here, if promo enabled, then do a bunch of work I've got, you know, a ton of work here to actually show my show my field and you know, and here I'm showing it. Here I'm showing it. So do a bunch of work, show my field and also check to see if they passed in a promo on the URL. And if so, then go ahead and set its value and do some work and then check that promo code. But if it's not enabled, then don't do any of that. Don't show the field and don't check it and then nothing else happens. So that's a couple of the things I check off in the URL. So that's, you know, that's an important one to actually remember is that clickfunnels gives us this geturl parameter function out of the box. So I use that quite a bit. You might as well use what's already there and already defined. And we know that if it doesn't find that parameter name on the URL, then it's gonna return the string null. I wish it didn't return a string. I wish it actually returned the JavaScript value of null. It's a little easier to check that way. But it it that their function returns the string of null and then Otherwise it returns the value, a string value of what's actually passed on the URL. So PCO n if it's. If it's set to one, it's actually gonna return the string one. That's why I'm doing a check here of string not equal to zero. So you can actually return PCON equals the string zero. So I've got that close. And this is an instance where I use the exclamation equals equals because I'm checking to make sure that the string returned is not equal to the. To this string of 0 versus the actual number 0. Because we don't get a number 0 back from getUrl parameter, we get a string 0 returned back from that. So, yeah, so that's kinda what's going on with Promo Code Pro. And so we're using that in this instance. We don't really use it. It's not something that's enabled on the page currently. But the code is behind the scenes there. And then let's jump through. So we got again some style sheets we're applying. And then the next one is pretty cool. On this one, this is the CF AJAX submit and we use it specifically. This is. I think I call this like the CF three step, three step order Ajax. And what this does is we set up a named function called setform bounds and we also have another name function for validate email. So this is. These are ones that actually pulled from clickfunnels. They name these functions, they have these functions included in the core lander js of ClickFunnels, but they don't expose them at the window level, so we can't call them. So I had to pull them in and redefine them. But this is basically setting up a. What's called a regular expression here. And this is. It looks like complete gobbledygook, but it's not. It's going through and finding, hey, we can have characters, we can have a bunch of stuff. We can have all these things be part of a valid email. It's doing a super long regular expression and then it's saying, hey, find me the value that got passed in of email. So I'm setting I equal to this regular expression. So I'm saying I test. So I can call a test on a regular expression and, and pass in a string and it will return me whether that string matches or not. So it's gonna return a true or false value here. So I'm just validating. Is this a validly formatted email address? And then this function is actually going through and looping through each of our input fields and setting the matching AR field in our hidden CFAR form so that before anything gets submitted, I actually submit these. Cause normally this happens when you click Submit form, but in this case we want to do this via Ajax. So we're going to set that CFAR form and all its values first, and then we're going to submit that form asynchronously behind the scenes. So we're just looping through all of our current input fields and their current values and set them the visible ones. And then we're setting them on the hidden CFAR form because that's what gets submitted to QuickFunnels. And then this function up here, this, this basically is what happens. This is what fires that off. So we say, hey, when somebody clicks on the Step one button, the first thing I want to do is I want to find out did their email validate? Did the email that they input into the email field grab its value and then see if it's valid. If it's not valid, just return false, which means just exit out of this function call and don't do anything else. But if that did, if that return true, then it's going to bypass this and then the next thing it's going to do is set the form vals, so, which says, okay, so now go ahead and loop through all my inputs and set their values on the hidden CFAR form. So I know that's done. Because ClickFunnels doesn't do it every time you enter values. It only does it at a certain point in time. Once that's done, then go ahead and make a post. This is the dollar post. This means. This is. You can actually call this differently. So this $post is a shortcut to call the $ajax function with a type of post. There's also a $get, which we will do a get call of the $ajax function. So we're doing $post, which means a form post, and we're gonna, we. We have to grab a URL and we can grab the URL of the current page. Because ClickFunnels wonderfully sets a data attribute on the head of this dash URL. So we know what the URL of the current page is. We can grab it from the window. But we don't want to do that necessarily, because we want to post to whatever ClickFunnels believes is the proper URL for this page. So we're grabbing that and then we're going to grab our CFAR form and we're going to serialize it. And so this serialize function is a jquery function that you might use often. Maybe not often, but anytime you want to do an AJAX post, the post needs to have a serialized string of attributes that it can pass into the backend. And so this is how we serialize that string of form inputs into a string representation of their names and values. So we're just saying, hey, grab my CFAR form and pass it into the serialize function or call the serialize function on it. And that's gonna convert all of my inputs and all of their values into an actual string representation, which is key value pairs in a string. And we're gonna pass that into this URL. So, and that's kind of just so you understand how clickfunnels works behind the scenes. Whenever you click Submit form, it submits the CFAR form to itself. So the, the current URL of the page, it's gonna say, hey, grab my CFAR form, submit that form to the current page. The current page processes it and says, okay, great, I got, I got this data. So now I'm handling the post. So when I handle my post, if I can successfully go through and create a contact record, then where should I redirect to next? And then it redirects to the next page so we can call that exact same thing. When we have form data that we want to submit, we just submit it to the current page. But in this case, we don't want to actually leave the page, we don't want to redirect and go to the next page. We just want to submit the email address so we can get a contact record created based on the email address they just gave us so that now we can follow up with them if they don't actually purchase. And so this is exactly how the two step order form works. This is built into ClickFunnels for the two step. It doesn't exist anywhere else. So I just created an add on where you can actually call an AJAX and do the same thing whenever somebody clicks on step one. And so that's what this code does here. It does that behind the scenes call to create a contact record based on the email address they just entered so that we can do abandoned cart emails if they never actually finished their purchase. So that is the CF3 step Ajax function. Does that one kind of make sense? I know there's a lot going on here, but hopefully it's clear as mud.
Well, not at the end. I, I finally got. Yeah. So basically every time they click the button in the three step. While the first two times they click the button in the three step, it automatically stores it to the database.
Yeah, so what it's not, it's only doing it actually in this case we're just checking specifically for the step one button call.
Okay.
Yeah, because that's where they entered the email address. Right. Is on step one. So that's the one we want to say more. Name and email address. That's where we want to store the contact info is when they give us their name and email address, we want to store that contact. And then step two is usually billing info, could be credit card and stuff like that. So we don't want to do it then yet because they maybe are not ready to submit their order. But if we submit it then, then it could potentially create an order and then if they click submit form it would create another order. So we don't want that, but we do want to store, we do want to call it when they actually click to submit their email address.
Okay, so then if they don't buy. So basically the whole idea is here. If they don't buy, then you're gonna hit them with a follow up funnel at that point. How, how is that determined then that they didn't buy?
Yeah, so let's jump through that real quick. So if we go in here, let's see if I can drag this to another window. There we go. So if we go in here and look at our order form on our automation tab, we actually have events that we can trigger actions and emails on.
Okay, so that's, that will trigger, it'll. It'll know this here then in the automation.
Yep, absolutely.
Okay.
Yeah. So you're gonna fire then on a saw page but didn't purchase. Because they landed on the page, they give us their email address. So we now know who they are and they saw the page. But they did. We don't, we didn't track a purchase after a period of time. And usually for these, for the SOL page but didn't purchase actually set a delay on them. So I would either add a new action or add a new email. And then on the email delay I'd say, okay, we're gonna give them 15 minutes to actually view the page and decide whether they want to buy before we fire this. Otherwise as soon as we submit the email, if we don't have a delay on this, it's gonna fire the email. Hey, I noticed you landed but didn't buy. Well, they may have actually bought by that point, but it may be 10 minutes later and they go back to check their email like, no, I bought happened. So I always give this a delay. You can give it a half an hour, you can give it four hours, you can give 24 hours, whatever you want to do. But set some sort of delay on the saw page, but didn't purchase type events so that, that way if they purchase after a period of time, then that never actually happens. Does that kind of make sense?
Oh yeah, I got it.
Cool.
I don't suppose clickfunnels has anywhere a list of. Because you're like showing built in functions in clickfunnels. I'm sure that list doesn't exist anywhere.
No, it does not.
No.
This has just come from experience and honestly I grabbed this years ago before I was even working with clickfunnels by just going and grabbing the Lander JS script, you know, JavaScript. That's the beauty and the downside of it is it's publicly available. If you're gonna load it on a page, you can grab it. And as long as it's not obfuscated, where it's like turned into gobbledygook like this, this is not obfuscated. But you could write all kinds of code that looks like this. Nobody's ever gonna be able to figure out what that does. But as long as not obfuscated, then you can look at and see what's actually going on. So I was able to see. Oh, okay, great. Yeah, we got this function here that validates an email address. Yeah, that. That would be usable again. So let me just grab that and then put it into my arsenal of ones that I might want to use that as well.
That's not, that's not obfuscated there. That actually is the. Yeah, this actually is JavaScript that validates the email.
Yep, this actually is. Yeah, this is a really, really ugly regular expression. It is not obfuscated code. It is really, a really, really ugly regular expression. The slash U here stands for Unicode. And it's like Unicode 00A0 through Unicode D7FF. So it's like. Is the characters included here in between these things? Yeah, and then this 2 comma 6 I think has to do with there's at the end the dot com. It can be a minimum of two characters and a maximum of six.
Well, yeah, I see the sign in there and I see the period in there. So it's testing everything in between those characters.
It's just a really, really ugly regular expression. But obfuscated code, you can actually go into and you may notice this, we use it in a few places where we're actually using codes that maybe we don't want to expose. You can go to JavaScript obfuscator and there's several different flavors of these pages that you can go in and check out. And so you can actually take your code and put it in. And so say this one, we wanted to obfuscate. So we're going to take this function and we're gonna obfuscate it. And then we could actually just take that code there and paste it in. So that takes that really ugly code and say obfuscate and then that's gonna give us this. And now if we copy that and go back in. Now this is the obfuscated code.
Yeah, it looks the same.
Yeah, yeah. But this is really actually much more difficult. Cause we could also take another. Let's take one that's more obvious to read. Let's take like this one. So this is legible, right? You can kind of read it and see what's going on. Now let's take and run this through an obfuscator.
Now how easy is it for somebody to unobfuscate?
It's really not that easy unless they know exactly. It can definitely be done. There are pretty advanced unobfuscated codes for a human to do it is not easy at all. But we can. Why is it not copying my. I want to copy this. Copy, copy.
Oh, you're in the output field, that's why.
Oh, duh. Yes.
I need to go back to the copy paste JavaScript. You gotta click on the first one.
That's why. That explains it. Why is it not working? Okay, so now we've got that code in here and this code is legible. We can figure out exactly what this does. Now we can obfuscate it. And now if we grab this and go back in and look and paste that in, it's like. Ok, I have no idea what that does. My variable names have been changed. I can see. Okay, so I can see CF er in here. But I don't know really what else is going on. A lot of these things, like even the data title has been replaced with a few things. So I can see step one button in there and see a little bit. But there's a lot of other stuff going on that I don't necessarily know what's happening.
Does that slow down the load time?
It does not. Yeah. Actually JavaScript doesn't care how this works. It's all. It's just using base Java code. JavaScript code. So it's basically setting up arrays. So this is, this is a variable name, underscore, and then it sets these random variable names. So we've got a function called. And we're calling. We're passing in this, we're passing in this. These are just variable names. So we're. It's basically just saying, okay, this, you'll notice now we have a function it's creating, and so it's setting this variable name equal to a. Fun. So we've got var variable name equal to a function that takes a parameter that was set somewhere else. That was. That's. Yeah, Somewhere else in the code is defined. And while this, do that, push this, do that. It's. It's doing a bunch of work, but it's all based on variable names. So JavaScript doesn't care. It's just variables. They don't. It's. It's just as fast running obfuscated and sometimes good obfuscators will actually be faster to run because they get rid of all the white space and all the, all the stuff that JavaScript doesn't really need. So it might actually be faster to run when it's obfuscated than when it's not.
So it minifies it and then it obfuscates it. But what if you set a global variable outside of this obfuscated code? Could you set. Well, let me ask, can you set a global variable in one script and then have it called in, use it in another script or not?
Yes. So what you don't want to do is have a globally defined variable that's obfuscated and then try and call it the original name later in the code, because at that point JavaScript doesn't know. It knows that it's the obfuscated version. So you have to be a little careful with that if you're gonna do it.
Obfuscate everything.
Yeah, that's definitely the safest way to go, because then everything is available. But the problem is you can't necessarily obfuscate with your script tags. You have to obfuscate what's within your script tags. So you have to be careful on how you piece your code together if you're gonna obfuscate it. Because if you, if you break things into independent script tags, you have to obfuscate what's in between. And when you, when you obfuscate independently, the variable names may not necessarily match up because when we go use this online obfuscator tool, it doesn't know that you're going to include 17 different scripts.
Well, my question is, why don't you use that? I mean, I guess you wouldn't want to use it necessarily in CF Pro Tools where you're giving it to somebody, but why don't you use that in the stuff that you're doing for clickfunnels, funnels?
And we do in some cases and in a lot of instances we don't. It's, it's, it's kind of a little bit of a share. It's like we could and like we do on or if you, if you will go and look for order forms like funnel hacking live tickets. We use promo codes there. You can actually insert a promo code. That code is obfuscated, so you don't know what the actual promo codes are. So we do have some instances where we do obfuscate that and then we have others where it's like, hey, this is just functionality that, you know, is kind of nice. If somebody wants to grab it, use it. If they understood what it does, then sure, let them, let them figure out how to use it. But yeah, we don't, we don't overly do it. And same reason, CFO tools. Yeah, at some point in the future CF Pro Tools may change a little bit. But it was started out as a, as a share, like, hey, I just want to share this and add value to everybody. So I've not done any of that to actually obfuscate any code because I want people to be able to easily modify it and that's the way to do it. If I obfuscate the code, it'd be really difficult for you to go in and then figure out what the code does, let alone modify it. So now down the road I may do that, but it's not something I've done so far.
Right, okay.
So yeah, but if you wanted to, yeah, things where you might, you know, and most users, honestly, we're looking at this, we're coders, we understand this, we understand what it's doing. So yeah, we can inspect the page and see, oh yeah, I could find a promo code. The majority of visitors that are, you know, if we're just building an E commerce shop, the very, very large majority of customers are never going to be able to figure out what that does, nor do they care. So they're not going to be looking at the code. And even if they did, if I was going to sell somebody something at a discount, I'm fine. If they find the promo code and get the discount, that's fine. I was still, it was a good Deal for me. Right. I still got a customer, so I try not to worry too much about it.
So.
So, yeah, so that's kind of JavaScript obfuscation and then a little bit of a nutshell and then the reverse is. So if we cancel this and let's see if this one actually has a de Obfuscation, I think it probably does copy and paste JavaScript code. This is Obfuscation. There's a de Obfuscator as well.
Yeah, I used it one day actually. Austin Dixon, I think he got the code from you to obfuscate the coupon code. He was like acting all smarty pants. That is like, oh, oh, you can't find it. So I grabbed the obfuscated code, I ran it through the de Obfuscator and then I sent it his promo codes.
Oh, yeah, yeah. So you can do that? Yeah, and there are tools to do that. Some of them aren't quite smart enough to, you know, find it if it was obfuscated with a different tool. And there's some. This one is pretty, pretty straightforward. So it's not doing anything super robust as far as obfuscation goes. So it's easily reversed. But there are other tools and there are paid libraries as well that will really make it very, very difficult for any other obfuscation tool to actually reverse it. So, yeah, so. And they can also use keys that are set elsewhere, you know, other variables that you can insert into JavaScript that actually unlock the code so that it operates properly. And so there's other ways to make it much more complicated for people to actually be able to do that. But it's kind of a cool tool. It just makes it ugly, basically, is all it's doing. So, and if ugly is enough. And that's what obfuscation means, right? It's, it's, it's, it just means to make it more difficult to read. It doesn't mean that this is going to be super secure. So. Yeah. And so let's see, what else do we have going on in this one? Here's one. You can see that we've got a click and a change happening. This is the on. This is an example where we're looking for a couple of different events. So why use on versus just doing click and dot change that do the exact same thing. So in this case, you know, we're updating the images on the page and we're updating some icons based on if somebody clicks on does it. And oh, this is actually a really good example of on. So we can say we're looking for a click or a change event. And you'd think we're looking for this on a click or a change on the document, but we're actually not because we've got an additional parameter here. We don't go straight to a function, which is the way we've seen it so far is it's on click call a function. But in this case we have an additional parameter in the middle, which is a selector. And the selector says on the clicker, change. So what this is saying is look through the entire document and add the click or change event to any element that matches this selector. So anywhere we have a datade type or PO that also contains an input with a name purchase product id. So this is basically saying, hey, grab me. Anytime somebody clicks on a product to select it and click on the click or on the change and then fire this update icons and wrapper. So that says anytime the product selection changes, then call that function. And then the first time through also call that function. So the very first time the page loads, we're gonna call that function regardless. But then after that, anytime somebody changes the product, call it again. And then this one just goes through and says, hey, find out what product was selected and then go and find out what its value was. So CF selected product and then go in. This is so that we can do the whole bit where we see.
Back.
To the right page here where we can see this image change. So this actually swaps out. So it's running a little bit slow because I've had the Wistia video running on this and we're recording all at the same time. But that's kind of the code that's kind of enabling some of that. And so in this one we do. So if it's this product, and I didn't actually write this code, this is Nick Sanders on our team. This one is actually just looking at the image. It's grabbing images and grab and changing their source based on which product is selected. So we have these two images, but there's actually four images we're worried about, one with it checked, one without it checked. And so we have two variations of these images for each one. And then so we're basically saying, hey, if the product selected product is 882, then swap out these two images with different sources. And if it's this one, then swap these two images out with different sources. And so that's what we got going there. And this one is looking at again, we're going to update address Fields we have. This is if you buy the digital only product. So this is saying, I believe we're using this one to. And I don't think this is actually in place anymore. If we switch to the digital only, let's see if our. Yeah, we're actually still collecting the address anyways. At one point in the past I think we actually changed. So let's actually look and see if we do this because we're not necessarily worried about shipping address. So if we go with both physical and digital and we look at these, we'll notice now it says shipping address and these should all be shipping address fields. So now they say shipping address. And I believe if we click and go to digital only now it just says address because the customers that come in and they're buying digital only, they may not be like, well why do you need my shipping address? I'm buying a digital only. So they understand that, hey, but I might need billing address or I might need address for my billing information. So in this case we really don't care that it's shipping. We used to, we used to swap these out to go back and forth between address and shipping address, but we decided that really didn't matter. All we really cared about was this heading because we didn't want to think people that we were requiring their shipping address to buy a digital product. So we modified that up a little bit. So that's all this code's really doing is we're changing out that headline based on, based on whether they pick digital or physical. And then this by default is saying, hey, when somebody first comes on the page, select the default product. So this is the CF select default add on. We're just saying by Default select product ID 2051014 and that's what we got going on here. So that is the entire ofa challenge order form in a nutshell. Some of it very, very condensed. But overall the big one, I think that's probably the most powerful and the most unique and the most kind of the best exercise to look at what's possible with clickFunnels and JavaScript and external spreadsheets specifically is the first piece, which is, hey, you could have multiple pages and this would be a great example. In fact, this is probably something I'll build out for those that like to run live webinars or want to have one registration form that modify that changes based on when a webinar is going to happen. And you can use spreadsheets to help to manage those sorts of things. And so you could have, you know, the same funnel with order forms and everything when things are going to close, when they're going to redirect to. Sorry, you missed it. Sort of offers. You could run through spreadsheets and say, here's my list of webinars and when they're happening. And then, you know, you could even have. There's lots of things you could do with spreadsheet management that you would normally do with an external database and a custom built system. But it's like, hey, we want to use clickfunnels to build the pages. We just want to be able to modify those pages a little bit based on what's the current date and what dates do we want the person to see. And we don't want to have to go back and modify the funnel every time we do a different date. So that's kind of what we have going on with ofa. And we're at what, just over three hours now. I do want to go get a chance to go through with Jeremy and he had the question specifically about the products and how we do handle that case. And let me see if I can find my Facebook page here. And let's see, get rid of some of this stuff. And let me see, do I still have. I thought I had that, the actual post open somewhere. Here we go. And so in this one. Towards a little bit farther. Yes, this. So this sort of come on Facebook. I think this might have frozen a little bit.
There we go.
Too many tabs opened. I'll close down some of these things. Get rid of my video for my ofa. That's going to help. I'm going to get rid of that. Go ahead and get rid of Google Drive for now since we've kind of run through that. Leave that. And let's get rid of Facebook here. Get rid of these. Okay. And so kind of wanting to do this. This example here of. Hey, we've got different sections, right? We've got Preferred Premium Platinum. How many tickets do you want of each one of these? And being able to do this. So is this, is this something you saw someone else build out? This looks like something you're trying to build in your own account. Is that, Is that right, Jeremy?
Yeah, that's something that I've been working on on my own.
Okay. Okay, cool. So let me see. I'm going to actually pull up over in another screen here. I'm gonna see if I can find this real quick. It's been a while since I built this out, but I want to find the URL so we can take a look at it. And so I actually need to. Like I said, we built something very similar to this out for our a couple of years ago for Funnel Hacking Live where we did first did the premiere of the our documentary and I think it was the first Funnel Hyper Live where we had Tony Robbins. And at the end you could go through this our funnel where you could watch and kind of get your tickets to see the premiere. And then at the end it was like, hey, do you want to buy a special package? We're going to give you the DVDs, we're going to give you all these things. And then part of that offer was you can pick from several different levels and pick different quantities. So let me see if I can find that really quick. And then we can kind of look at an example of how that works and look at some of the code that's used to make that happen. Just give me a moment here. So bear with me. In fact, if you guys want. Why don't we take Are you guys hanging a good hanging out a little bit Because I want to have a chance to get through this but I also want to make sure that everybody gets what they came for. But I also don't want to take up more time than what you guys have available. So are you cool hanging out for a bit for this? Dan, Jeremy, Richard?
Yeah, I'm good to keep going.
Okay. Yeah, yeah, I'm good. Okay, awesome. So why don't we do this while I find this page? It's probably a good opportunity. Let's just take a. I'll give you guys just a quick five minute break while I find this funnel. I'm also going to take a quick nature break so so we can get through this and then we'll over here as well. And so this is the actual page. And so this is what happens when somebody goes through the our film.org they can opt in and get a 48 hour pass to watch the watch the videos for our for their operation to Saint. And then at the end they have a button below each of the videos to learn how to help.
How to.
Help the the ou our organization. So what they can actually do is come in here and actually pick from three different categories of things that they want to do. And so we want them to be able to select multiples of these and so it looks like it might. Oh actually I'm not quite sure why we can select multiples of these but you should be able to say oh you know why. Ah, I'm glad. So this is a good note. Clickfunnels has recently changed the way they build these selectors and they're gonna have to change back. So even though these look like radio buttons, they're actually checkboxes. And so they're checkboxes that are set with a display of radio. And so that's why we have multiple select here, even though there shouldn't be. So I'm going to need to make. I'm glad we looked at this page because it's probably been broken for quite some time now and needs to be fixed. And so but the goal here was that they would be able to select one of the options from this first list. They'd be able to say, okay, I don't want, I want to be no monthly donation or I want to be an abolitionist at 10, 25, 50, $100. But then they'd also be able to say but I also want to buy the collector's edition plus bonuses and be able to check that one. And it's in a separate list. But these are all, if we look at this, these are all just product selector elements. So product selectors with clickfunnels, the way they work is that they list all products that are part that are on the order form. And so we've got three of them. And so normally this would completely break. You know, you'd see all the products, you'd see all these things listed in each column and you'd only be able to select one of them out of any one of these. And so here we have a fund, a need you can make a one time donation. So these are subscriptions. This is a one time. This is a one time. So I've got custom code on to basically limit these things. And we can see if we look at these, I believe if we look at each of these elements, they'll have specific CSS titles. So this is the order select for abolitionist. This is the order select for I think collectors, this might be what it's called. Yeah, Order select upgrade. So for upgrade your order and then we've got an order select for to fund a need. And so we've got order select fund. So we've got identified each one of these product selectors and then we've got the buy buttons. So this is a complete your order. And so this is the URL of the action on these. It doesn't actually submit the form, it just goes to create. Go to the complete your order section which is this next step down. And so we've got order summary here and the order summary is going to do what it, just what it did we showed earlier. It's going to loop through every product on the page that is added to the cart and it's going to show it here. So the big thing we needed to figure out is, okay, well how can we show different products in each of these selectors when the default out of the box click funnels is to show one? It doesn't work with multiple product selectors normally. And how can we make them all list independent individual products based on the column it's in? And so what I did is I set up basically some configuration to say, hey, I've got a few different things that I want to do here. I've got a list of product IDs that I want to show in my abolitionist product selector map. And I've got one product that I want in my upgrade. And then I've got three different options for my fund map. And so we can kind of see that represented here that I've got these four different products that I can go to for my different levels. And then I've got a fifth one that is my no monthly donation. And then I've got just one product for my upgrade and that's this one here. And then I've got three products from my fund. The need and that's. Those are listed here. So I've just broken those out and said, okay, I've got, I've got an array of objects here. And this could honestly probably be just an array because it's actually they have named keys, so I do need to have this to be an object. So I want to be able to reference them by name later. But this is just an array of object product IDs and then I have a product selector, empty text. So I want to be able to have an option for no monthly donation, no upgrade, and not at this time, we don't show those anymore here. We used to have a no option below each of these that they'd have to select, but we decided we just would leave them unchecked if they didn't, they didn't have those. And then this one the default. So I want to set a default for the product selectors that I which one do I want to have select? So this one I just select the product ID for the $10 a month set there. And so then I go through and each of, for each of my data title where the orders. The title starts with order select. Then this is code that I actually use a lot is this Var title parts equals and then I'm grabbing the data title and I'm splitting all the values by the space. And then I'm. So I set that as that returns me an array of strings and then I need to loop through that, that array of strings. And I'm looking to see does the, the current value, does the current string that I have, does it have an in? Does it, does it start with or does it contain order-select-? So that's where I'm doing value.index of basically takes a string and says, hey, on the current string, can I find this, this string here? And if, if the index of will do two things. If, if it finds a string, it'll return the index in that string that it starts at, which is gonna be zero. Could be the first character is where it starts. Order select could be the very beginning. So it has an index of zero there. Or it could be, you know, could start with CF dash order dash select. And so then it would have an index of 3. So but if it doesn't find this string at all within this string of value, then it's gonna return a negative one. So this basically says, hey, if I found inside a value, if I found the string order dash, select dash, then go ahead and set the product category, product cat, split it by dashes. So take the value split by dash, which means order becomes a value, select becomes a value, and whatever comes after the dash becomes a value. And then the dot pop means pop off and return the last item in the array. So split returns an array where it takes this string and it takes each dash in it, and whatever is comes before that, it returns as one item in the array. So item one, item two, and then whatever comes after this dash is item three and gets rid of the dashes. We don't need the dashes. And then pop says, just grab me the last item of the array and return it so that this product cat becomes the value of that item. So the reason I do this is because I have a lot of instances where I want to have multiple CSS titles applied. And we might even have that here. I don't remember if we did or not, but I might want to have this be also have, you know, something CTO CFoto since Dash 1900, something like that. Well, if, if all I was doing was checking to see, you know, does that my order, does my title contain order select or is it equal to order select dash, then this is gonna mess that up. So that's why I say, oh, hey, I need to take my entire title and then I need to split it by spaces so I can grab each individual piece and then see if that piece, if the current one I'm working on contains this, and if it does, then that's the one I'm looking for and let's grab its name. So we're really just trying to get to what's this piece of the. The current order select. Does it. Is it abolitionist? If so then grab my product selector map for abolitionist. And so it goes up and says, oh, grab the product selector map for abolitionist. Grab that array and that's that array of product IDs. And then it loops through all of my product or my data CF product templates, which is what I see here. If I go and I right click on this and do an inspect, it's gonna loop through and it's gonna look at all the elements that have. And if we close these down, we'll see we've got 1, 2, 3, 4 here that have data cf dash product dash template equal true. So I'm saying basically, hey, go through and find me all my data CF product template true within this. Which means in the. This, in this case is my element here, because that's the. Each I'm within. So I'm working on the current product selector only. I'm not looking at all the other product selectors, I'm just working on this one to start with. And then my second time through the loop, I'll be looking at this one. My third time through the loop I'll be looking at this one. So go through each of the product templates within the current product selector. And so we're going to set a couple things up. We're going to say, okay, so what's the current input? Find it. So this dot, find the actual input field and then from that input grab its value and set that as the product id. And then I want to go ahead and input. I want to set a new data product category and I'm going to set that to prodcat because I want to be able to work on that later. And I'm going to set a name of it to the. Add the name of the current input plus the product category, because I want to be able to output that later. And then I'm going to say, hey, if it's. If the, if that current product ID is in my product ID array, which is the array I got. Which is. What's the, what's the array of IDs that I want to show in this, in this product selector. If my, if this product ID is in that array, or actually if it's not. So if it, if an in array is a jquery function and it basically returns an index or a negative one, if it's false, if it doesn't find this product ID and this array of product IDs, then it will return a negative one. And so what this is saying is, hey, look through my product ID array, which is my, is this array of IDs here. So loop through each one of those and if the current product id, which is my product ID of my current input, if it's not, if that product ID value is not in this array, which means it's equal to negative one, it's not in that, then just remove that, that product ID entirely. Remove that selector entirely. So we started out because normally we'd have these four plus this one, plus these three all in this first product selector, but this code is actually saying, hey, if I didn't find the product id, if I didn't find this product ID in this array, then just remove that selector. So it doesn't even show up, it doesn't even exist in the DOM anymore. It's gone. And then, you know, product options select. And this is actually removing. What happens here is if you actually inspect this code, you'll notice we have this, we have another selector below this. Let's see here. Oh, it's actually gone. If you look on any other product selector, if you inspect it, you'll see that there's two options for a product selector. There's the radio button option of this, but then you can also say, nope, I'd rather use a drop down option. And in that case we don't want the drop down option to be here because it still contains those elements. So we just, we're not using it, we want to remove it entirely. So I just have a line of code here that says, hey, by the way, just remove all of the el order product options, select elements from the page, remove them. Remove removes the element from the page, not only so it's not visible, but it removes it entirely from the dom. So it doesn't even exist anymore. And so that's what that code does. So that's how we went to actually get to the point where we stripped out these extra selectors. We can have three different selectors on the page, but they each have different products. And so that's, that's kind of how this thing works. And then there's a bunch of other stuff that goes on based on, you know, is the product categories abolitionist. So then go through and find these things. And we want to, we want to, you know, append. In this case we're going to find the, the order select abolitionist product selector and we want to append that empty product template that doesn't have a value or has a value of zero so that we have this no monthly balance or no monthly donation option so that they can choose that. But it doesn't actually match up with the product id. Again, this is broken. It should totally switch these things right now, but it doesn't. So that's the ability to say no, I don't want to add this donation, but I do want to, I do want to add this one and I do want to go ahead and do a 2500. So you could say I don't want to do anything monthly, but I do want to buy my, buy my upgrade or I want to buy my upgrade and I want to fund, I want to donate a one time 2,500. So that's kind of what this order form does. And so we've got some extra work that's going on here. But I think, Jeremy, for you specifically, the one that was probably most useful would be this first chunk of like how do we actually modify our order selectors so that they only include certain products? And then what you would do is do this code something similar to this with this setup where you say, hey, which product selector should have which product IDs. And then you would include, because it looked like, if I remember correctly, you have on your page, you want to be able to do quantities, right? So somebody could buy multiple quantities of a ticket or a different type of ticket. Yeah. Is that what you want, you want to be able to do quantities as well, right? Yeah. So in that case then you would just, you would do the first chunk first, which is to show which products you want to include in each of the product selectors. And then the next step would be to include the CF Cart mode add on and cart mode is only going to operate on those things that are still on the page. So you've already removed the product selectors from each appropriate one. So cart mode will then just go through each one and should, should work properly for you there. If it doesn't, let me know because it might actually not show. And I think that might be what you're seeing here is it actually doesn't show the quantities for each of the additional ones. I think that might take a little.
Bit of tweak because so what I Ran into is. I was able to use just the regular product selector. Did a little bit differently. I did it by adding a class to each product ID type.
Okay, gotcha.
Title of my ticket type. And then I was able to use the class in CSS to hide the ones that I didn't want.
Gotcha.
But.
But then as soon as I put CART mode on, I ended up with my quantities all showing up multiple times. If I had four ticket types, I had like one through 10, four times on. But then no quantities on the other three product selectors.
Yeah, gotcha. Yeah, gotcha. And that's. Yeah, you're absolutely right. And I've run into that. ClickFunnels doesn't really support multiple order forms at all and multiple product selectors. So all the CF add on codes, CF Pro Tools add ons, I should say, are set up really to just rely on the fact that there's only going to be one product selector on the page. And so CART mode is no exception to that. Let's do this. Let me. I'm going to pull up the latest version of CART mode and see if we can modify that real quick to actually work properly for you. So let's load this up. Let's see here. So CART mode, because what we need to do is modify how it finds the actual product selectors, right? And right now I think it's set up to only find the first one and not. Not actually loop through them. So this one says. Okay, so, and so let's, let's kind of just review this code. So we're gonna say. So we're finding here product label. So that's finding the first, the first option, first item. So data product orpo label option, product options. Yeah, so this is going through and finding the very first row to put a quantity above it, then this one. So we really need this to loop through every. Every ORPO that's on the page. So let's do this. Bear with me. This might be a great exercise or this might be an absolute disaster. So we're gonna see if we can in the next 30 minutes or so modify this to work for multiple product selectors. So we're gonna, we don't. We're gonna set our start stop map the same. Okay. So we're not gonna mess with that. We're gonna set our start stop to be the same. But the next thing we're gonna do is we need to loop through each data de type equals orpo each and then we're call a function within that and we're gonna do that. The next thing we want to do, and this is something I should have added in a long time ago, is we want to actually set a datade type because what we have on two step order forms and actually we really don't even need to worry about two step order forms in this case because you're never going to have more than one product selector on a two step order form. Right. But if you insert your own product selector on a, on a page or on a one step order form, kind of like what you're doing, it's always going to have a day to day type of orpo. So this is going to be a completely modified version for specifically what you're doing. But what we want to do is go through each of those product selectors. So we need to do a dot each. We need to find all of them and then do it on each of them. We need to. We need to take action. So that's where we're gonna go through this. And let's just update cart quantities. Update cart quantities. And we really want to do. Just make sure I select the right stuff and not too much. This is just modifying this checked small sad class hide. So we want to take that out and we want to do that inside of our loop. Oh, and we've got an each here. Yeah. So we need to. We're missing a chunk here. So I missed this piece. So we need to end our loop. Okay, so that ends this each here. So the other thing that's kind of cool about if you're not using Atom or something like that, a good code editor, which hopefully all are by this point is if you notice in Adam specifically, and I think it does this in most code editors, you'll notice it underlines this parenthesis here. And if we scroll down it will actually underline the matching. So now I know. Okay, I've got my matching set. I ended the right thing. Same with brackets by M next to one. It's going to show me the bracket is like. Okay, so I know that I've got my right things here. So now I'm ending my right each. Okay, so I'm good. So I want to go through each of my data type or pose. And I'm gonna go ahead and set the var. This I'm gonna do. So I can reference this later properly in my filters. But I probably don't even really need to do that. So I'm gonna do. I'm gonna strip this out. Cause I'm really only worried about. Well, I don't even need that I'm gonna do. I want my. I might not need this. I'm going to leave it there just in case. So what I did there is I just stripped out and said, hey, I don't need to worry about finding my orpo, I'm within one already. So I just need to say, hey, within what I'm on, find the product, opt in label, product opt in item. Give me the first one. And before it put my quantity selector, my head. So my quantity head so that I see this, so that I see a quantity here above my selectors. Okay, so that's that. And so I kind of need to do the same thing here. I need to get rid of this. So we're basically replacing this datade type equals orpo and we're having all of those replaced. And again, I don't need this second batch after the comma because that's just for the two, that's just for the two step order form. But I do need to add comma, this so that it references the current product selector that I'm working on. So go through each of those product options where it's not hidden and its name is Product ID and loop through those and change its selector type. And then the rest of these should be. Okay, so we've got a selector where the quantity select anytime we have and this is any time we have input where its name is, purchase product IDs and it's checked, set it to. Oh, so this is all the CFAR products. Set them all to false to start with because we want it. We need to have a quantity of zero to start with. And then anytime. And then remove the radio options because those were legacy left behind. Update the current quantities that should stay the same. So all this stuff should stay the same. So this should all be good. So I think this will work for you. So if we were to go in and I'm going to grab just what's within this function and we're going to actually try and run this on. We're gonna try and run this on this page here and see what happens. Didn't actually do anything, which I expected it would. So now we start going in and adding our console logs, right? So we go in and say, okay, so console log. I just want to make sure that, hey, did I, did I actually find something? Am I, am I in this loop? Did it actually do anything? So I'm gonna go ahead and I'm gonna do that and I'm just going to grab this function, I'm going to grab everything here again. And I'm going to go ahead and reload this page. So we get kind of a fresh batch without the JavaScript that I just ran on it. Okay, so now I'm gonna load that in and now I'm gonna loop it. Okay, so now, yep, we're finding product selectors. So I found my product selectors and output those. So that's working. So now we need to figure out, okay, well, why is it not replacing. Oh, you know why. I know why. Maybe, maybe I know why. It did put quantity up above there so we at least see that. Okay, so that's good. So it's not finding these. For some reason it's not replacing. My radio buttons, which are actually check boxes with quantity selectors. So I'm looking to make sure my selector is right here.
Is it the way that it's referencing this in those variables?
This is actually referencing what's being passed into this function. So that shouldn't have any problems. But we're going to make sure that. So I'm gonna do the same thing here. I'm gonna console log. I'm gonna console log and make sure that it's actually finding. So this reference to this should be what's passed into this function because we're. We're inside of this each here, but once we jump into this each, then we're inside of this function. So this inside of this function means an entirely different thing than this this means. So yeah, but good, good call. But I don't think that's our issue. But we do want. Let's go ahead and output that and make sure we're, we're actually finding our radio buttons because I think that's what's happening. I don't think we're actually finding them. So I'm gonna reload this page one more time so we get fresh with no JavaScript having been run on it. And once it finishes loading. Ok, so we're finished loading. I'm gonna run this again. And yes, so we, we output. We. We did a console load for our three product selectors, but we didn't do one for our inner each. So that means it didn't find anything based on this.
Would that be a problem with that fact that those are checkboxes displayed as a radio?
It does, but I don't. I'm not seeing why, because it used to. These used to say, have an input type equal radio, which got changed. It's now input type equals checkbox, but I don't see that here. So there's something about this. So let me just do this. Let's do copy this and see if that selector is just not good at all. So we've got to get rid of the disk. We're just going to say, hey, find me all these things. And it didn't find any. So that's our problem. Heal product option Opt in products not hide that include name Purchase Product ID Oh, I reset the name on all of these, so now it's name Product ID abolitionist. So that's easy enough. So this is gonna work for you, it's not gonna work for me. So let's do this Name star. So I'm just gonna modify this version to work this because I've already really tweaked this particular copy. I think this is gonna do the. Do the deal. You wouldn't need to do this last little bit because you. You're not actually changing the name of your product selectors. Let it finish load. Okay, there we go. And now we have quantities and it should be. And let's see what happens to our order summary. Yeah, I'm not surprised that that didn't work properly because there's so much other custom code on here, but that should do the trick for you with that modified code. So there's a couple ways we can do this really. I mean all, all we really did in this. So you tell me what works best for you. All we really did in this was wrap the entire bit that was already there. So this is our original. And so the start stop map, the start stop, all that stuff stays the same. All we did was outside of this wrap all of this chunk, wrap all of that inside another loop. And that loop just says, hey, for every product selector on the page, do this. So that, that's our line that says for every product selector, do this. And then within that we filter down all of our selectors on the comma this and comma this. Instead of. Instead of filtering down and finding all of them, we need to filter down and only find the ones that are contained within this current loop, this current product selector. So you tell me, is this a good exercise for you to walk through? Do you feel like you have enough confidence to say, yep, this is. Yep, that makes total sense. I can just modify the currency of cart mode, make it work. Or do you want me to send you some updated code so that you can make it work?
I think my. I would rather try it myself first and see if I can because the exercise will be good for me. Yeah, if I run into, you know, Something where, you know, I can't. It doesn't work the first time or I can't debug it and figure it out. Then I'll post it back in the group and.
Yeah, and then we can, yeah, and then you can drop your page in there and then I can even do a follow on video and say, okay, yep, here's what's working, here's what's not working, here's how we tweak it and go from there. So. So, yeah, but I think that that will take care of your problem. So I think that will make it so that you can use pretty much what you already have. The one thing that I would probably do is go through what I did instead of just hiding those selectors for your different products. Because what you don't want to have, because what happens with the cart with the CF order summary is it goes through. Or I'm sorry, with cart mode, we'll see down below here. Cart mode actually goes through every one of your selectors. So even though the ones you have are just hidden with css, they're still going to get a selector. And so it loops through the up to date cart quantities, goes through each and every one of those quantity selectors, even if they're not visible and it looks to see what's the current quantity. If it's zero, then uncheck that item in the form in the CFAR form. Right. Or actually remove all those items and then only add back the ones where you have a quantity. So what's going to happen is you're going to end up. It's not going to work quite right. So you actually need to remove those products versus just hiding them and then insert the CF cart mode and then everything should work just fine.
Yeah, that makes sense.
Okay, cool.
I think, you know, after, you know, going through it that way, I think I may know why it wasn't working. Because. And the reason I ended up doing it by having to add a class is because what it did is I had. I couldn't figure out if the split was working correctly to grab the product ID from the data title.
Oh, gotcha.
And so what I ended up doing is just naming them each unique and then duplicating that function each time for each product selector.
Gotcha.
Adding the class. But I think I know where to go back and check and make sure that's working. It may have been working, but the result was whatever the last function that ran is the way that every selector was displayed.
Okay, gotcha.
I was getting the Same product on every product selector. Even though the data title was different, I think I can, you know, debug that and get that working. Perfect.
Perfect. Okay. Well, cool. Well, yeah, hopefully. And I think that kind of runs us through some pretty good examples of taking some of the things we did last session and going through these and looking at some of these specific examples hopefully gives everybody a pretty good feeling for. Okay, yeah, here's what's powerful, here's what's possible and, you know, give some pretty good jumping off points and some pretty good lessons of things that can be done, as well as how we've structured some of the CFRO tools, add ons and then obviously you're in CFRO tool, so you can go through all the different add ons and look at like, what's been done. We kind of talked about some of the things that I use quite a bit quite often and you'll see them repeated in all the different add ons, like the how do I use the title parts to figure out what's the title of the current thing? That's how I use anytime where I use the CSS title to configure things like product IDs or values. So, like for the CF Funnel Total Tracker, we use the CFoto Sense to be able to say this, somebody clicking on this button, it's add this much of value to the actual Facebook pixel tracking, you know, things like that. I use that title parts a snippet over and over and over again to actually grab those values. So. And you'll be able to use it as well on what you're building out there. So definitely go back and kind of review some of those things and you can grab those out and just kind of swap out. You might need to swap your variable names or whatever, but it's a pretty reasonable chunk of code. I use that one quite a bit and hopefully this has been good to kind of get everybody's feet wet a little bit on how to use this within clickfunnels and how we can modify and what currently works and how does it work within clickfunnels with product selectors, CFAR forms, being able to AJAX and be able to pull data in via spreadsheets and all that. So anything else that you guys can think of that I can answer right now while we got you, because otherwise I think we're gonna call this one pretty good. Do those as well as we get some interviews scheduled, we'll definitely have some more bonuses coming in. I'll put those in the members area. So as usual. As soon as I get the recording back in this video, I'll put it back in the members area. If you have any questions in the meantime, reach out inside the Facebook group. Definitely. Jeremy, let me know how it goes with modifying that cart mode code to work for what you have. If you run into. If it works, let me know. If it doesn't, let me know, and we'll make sure we get it working for you. But I think making that little tweak should. Should do the trick for you. So, yeah, so I'll definitely be. Definitely be in the groups and look for some of those bonuses coming here soon. And if you have any questions in the meantime, just reach out.
Awesome. Thank you, Jamie.
Awesome.
Thank you, sir.
We'll see you soon. Take care. All right.










