The purpose of this article is to provide users with all basic knowledge necessary to implement and use the Booxi booking button in an HTML environment.
This article will cover the basics of HTML, discuss what elements and tags are used in the implementation process, and how to implement a basic button in an HTML document.
Thenm features and properties will be presented and explained with the use of practical examples and user-cases. Finally, the article will conclude with how to implement and use google analytics efficiently.
The Booking Widget javascript library can also be accessed via CDN if requested.
Basics of HTML
An HTML document is a file containing hypertext markup language. Such code is based on elements and tags, which provide instructions for formatting the document. While there are hundreds of tags in usage today, the content of this section will be limited to tags relevant with the implementation of the Booking widget. Let’s briefly define some of the HTML concepts that will be used throughout this document.
HTML Elements and Tags
HTML Element
An HTML element is the collection of an opening tag, a list of attributes, an end tag and everything in between.
HTML Tag
An HTML tag (either opening or closing) is used to mark the start or end of an element. In the example below, <p> and </p> are opening and closing tags of an HTML element. Options associated with an element are always included in the opening tag.
<p class="example"> A paragraph’s content goes here </p>
In practice however, both terms are mutually interchangeable given that an element can’t exist without tags and the purpose of tags is to create elements.
Tags Used When Implementing a Booxi Booking Button
A minimum of three tags are required to complete the implementation of a Booxi Booking button. They are listed below along with their expected content.
- <head>
The <head> tag is the first section in the code containing information about a webpage's properties and links to external related files. This tag will contain Booxi and Google Analytics scripts.
- <body>
The <body> tag is used to contain a webpage's content, including hyperlinks, images, tables, text, etc. It is required in every HTML document, and there may only be one <body> tag per page. It will contain the widget’s configuration and a nested element, such as a button, onto which the widget will be mapped.
- <button> (or any other block or inline tag)
The <button> tag is an inline element used to create a clickable button. It will be used to map a method to launch the booking widget.
- <script>
The <script> tag is an inline element used to embed executable code or data, typically javascript code. It will be used to include javascript libraries used by the booking widget.
Basic Button Implementation
To implement a button, we will make use of the code generator. It is available online by clicking on one of the following URLs. You must select the URL based on your Booxi account’s hosting region.
Next, make sure you have a copy of your API Key on hand as it will be used throughout the implementation process and is mandatory to create a Booxi Booking button. As usual, it can be found in Booxi’s Back Office, in "My Business", under the "Business Details" section.
To implement a basic button to any webpage, you will need the following 3 blocks of code extracted from the code generator. Simply follow these steps:
First, you will need to include two scripts within the <head> tag of a webpage. If the <head> doesn’t exist, you can simply create one by copy-pasting the below code.
Including Scripts
<head>
<script src="https://www.booxi.com/api/booknow.js" async=""></script>
<script src="https://www.google-analytics.com/analytics.js"></script>
</head>
Next, within the <body> tag, include the following code to configure the widget. It must be copy-pasted within a <script> tag as shown below. Make sure to replace the API Key with your own.
Configuring the Controller
<body>
<script>
var bnHandler = null;
window.bxApiInit = function ()
{
bnHandler = booxiController.configure({apiKey: "YOUR_API_KEY_HERE"});
};
</script>
</body>
Finally, in order to prompt the booking widget, the book() method must be mapped to an HTML element. The code generator uses a <div> tag for this task but ideally, you should map the method to a button or any other element on your own webpage. Copy-paste either of the below code segments :
Mapping the booxiController.book() Method
As found in the code generator
<body>
<div class="bx_bn_button use_bx_ico" onclick="booxiController.book();">Book Now</div>
</body>
Implemented code on a button element
<body>
<button onclick="booxiController.book();">Book Now</button>
</body>
Once all three blocks are included, refresh your page and test your button and widget.
Create Your Own Button
Creating custom elements is easily achieved by using CSS. CSS defines how HTML elements are displayed on screen. To create a custom button, simply follow these steps.
Start by adding a <style> element within the <head> tag of any webpage. Within that newly added element, define a new class “.button” and assign its properties as you see fit. For a full list of properties, please consult the following page.
<head>
<style>
.button
{
background-color: #4CAF50;
border: none;
border-radius: 8px;
color: white;
padding: 16px 32px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 16px;
font-family: "Gill Sans", sans-serif;
margin: 4px 2px;
}
</style>
</head>
Then, simply assign that style to any HTML element using the keyword “class”.
<body>
<button class="button" onclick="booxiController.book();">Book Now</button>
</body>
Here is a comparison between default buttons and a custom one created from a user-defined style.
Default Booxi Button |
Custom Button |
Default HTML Button |
With a basic implementation completed, take a look at how the widget can be further customized with the use of properties.
Customizing the Widget
Here are a few options available to customize the look and feel of the booking widget.
Colors
The widget’s primary and secondary colors can be changed by assigning hexadecimal values to the properties named “primaryColor” and “secondaryColor”. Both properties must be assigned with the “addingBookingProfile” method as shown below. Take note that the “booxiController.open(...)” method will be used in place of “booxiController.book()” as the widget needs to be supplied with parameters.
<body>
<script>
var bnHandler = null;
window.bxApiInit = function ()
{
bnHandler = booxiController.configure({apiKey: "YOUR_API_KEY_HERE"});
bnHandler.addingBookingProfile('YOUR_ID_HERE',
{
apiKey: "YOUR_API_KEY_HERE",
primaryColor: "#0024FF",
secondaryColor: "#0066FF"
});
};
</script>
<button class="button" onclick="booxiController.open('YOUR_ID_HERE');">Book Now</button>
</body>
Color values must be formatted as #RRGGBB
The value of primaryColor is applied to buttons and markers, secondaryColor is applied to active selection.
primaryColor |
secondaryColor |
|
|
Font Family
The booking widget uses “Century Gothic” as its default font. To set a different font family, assign a value to the property “fontFamily”. The following code showcases how to implement this feature using the “addingBookingProfile” method.
⚠ |
Only browser safe fonts can be assigned to the property “fontFamily”. |
<body>
<script>
var bnHandler = null;
window.bxApiInit = function ()
{
bnHandler = booxiController.configure({apiKey: "YOUR_API_KEY_HERE"});
bnHandler.addingBookingProfile('YOUR_ID_HERE',
{
apiKey: "YOUR_API_KEY_HERE",
fontFamily: "Comic Sans MS, Comic Sans, cursive"
});
};
</script>
<button class="button" onclick="booxiController.open('YOUR_ID_HERE');">Book Now</button>
</body>
Loading Animation
The widget’s loading animation can be changed with a custom .GIF file. To do so, assign the .GIF file’s location to the property “loadingAnimationUrl”. The below code showcases how to set its property.
<body>
<script>
var bnHandler = null;
window.bxApiInit = function ()
{
bnHandler = booxiController.configure({apiKey: "YOUR_API_KEY_HERE"});
bnHandler.addingBookingProfile('YOUR_ID_HERE',
{
apiKey: "YOUR_API_KEY_HERE",
loadingAnimationUrl: "https://YOUR_DOMAIN/YOUR_STOTAGE/loading.gif"
});
};
</script>
<button class="button" onclick="booxiController.open('YOUR_ID_HERE');">Book Now</button>
</body>
UI Style
The widget comes with a default UI style that can be fully customized. For a slightly different look, the property “uiStyle” can be set to “classic”. The code below showcases how to set such a property.
<body>
<script>
var bnHandler = null;
window.bxApiInit = function ()
{
bnHandler = booxiController.configure({apiKey: "YOUR_API_KEY_HERE"});
bnHandler.addingBookingProfile('YOUR_ID_HERE',
{
apiKey: "YOUR_API_KEY_HERE",
uiStyle: "classic"
});
};
</script>
<button class="button" onclick="booxiController.open('YOUR_ID_HERE');">Book Now</button>
</body>
Default UI |
Classic UI |
|
|
Standard Title Font Bold “X” Button Button with Squared Corners |
Bold Title Font Standard “X” Button Button with Rounded Corners |
Language
The widget’s language can be set by assigning one of the below codes to the property “language”. Supported values are as per the following table (Language: Language Code). Please take note that changing the widget’s language will not affect the preferred communication language set by the client.
English : eng |
Francais: fre |
Español : esp |
Português : por |
Nederlands : nld |
Deutsch : deu |
Italiano : ita |
Română : ron |
Polskie : pol |
粵語 : yue |
日本語 : jpn |
Ελληνικά : ell |
Dansk : dan |
Svenska : sve |
Български : bul |
Русский : rus |
한국어: kor |
Český: ces |
Türkçe: tur |
The below code showcases how to set the language property using the “addingBookingProfile” method. Take note that the button’s label is set manually.
<body>
<script>
var bnHandler = null;
window.bxApiInit = function ()
{
bnHandler = booxiController.configure({apiKey: "YOUR_API_KEY_HERE"});
bnHandler.addingBookingProfile('YOUR_ID_HERE',
{
apiKey: "YOUR_API_KEY_HERE",
language: "deu"
});
};
</script>
<button class="button" onclick="booxiController.open('YOUR_ID_HERE');">Reservierung</button>
</body>
Using the “bookingFlow” Property
Using the bookingFlow property allows you to set how the widget will be prompted.
Values |
Definition |
locations |
Widget will be prompted with “store selection” as the first step in the booking process. Without the use of tags, all store locations will be listed. |
zip_code |
Widget will be prompted with “store selection” as the first step in the booking process. Stores listed will be limited to these within a specified zip_code. Access to this feature is limited to accounts for which it’s been activated. |
A group of locations must be defined in the Back Office for this feature to work as expected.
The following examples showcase how to manually set the “bookingFlow” property in your HTML code. Remember that the same result can be achieved with the code generator if you need assistance.
<!-- Set booking flow to “locations” -->
<body>
<script>
var bnHandler = null;
window.bxApiInit = function ()
{
bnHandler = booxiController.configure({apiKey: "YOUR_API_KEY_HERE"});
bnHandler.addingBookingProfile('YOUR_ID_HERE',
{
apiKey: "YOUR_API_KEY_HERE",
bookingFlow: "locations"
});
};
</script>
<button class="button" onclick="booxiController.open('YOUR_ID_HERE');">Select Store</button>
</body>
The above code will display all store locations in the widget (or up to 50 if their count exceeds that number ). If stores are categorized or sorted by regions, the property “locationCategory” can be employed to require the user to select a category or region instead of listing all stores at once.
Using the “locationCategorySelection” Property
The property “locationCategorySelection” can be used to supplement a “locations” booking flow. By activating this property, users will be required to select a region, regardless of how many stores a merchant has. The widget will display all stores assigned to the selected region. Take note that the property “bookingFlow” must be used and set to “locations” for this feature to work as expected.
The following example showcases how to activate the “locationCategorySelection” property.
<!-- Set booking flow to “locations and enabling locationCategorySelection” -->
<body>
<script>
var bnHandler = null;
window.bxApiInit = function () {
bnHandler = booxiController.configure({apiKey: "YOUR_API_KEY_HERE"});
bnHandler.addingBookingProfile('YOUR_ID_HERE',
{
apiKey: "YOUR_API_KEY_HERE",
bookingFlow: "locations",
locationCategorySelection: "required"
});
};
</script>
<button class="button" onclick="booxiController.open('YOUR_ID_HERE');">Select Store</button>
</body>
This feature can be disabled by setting its value to ‘default’ or by removing it entirely.
To further control and filter the content displayed, you must employ location and service tags.
Using Location and Service Tags
In order to make use of tags, they must first be defined and assigned in the Back Office. Make sure your tags adhere to the following rules. They ought to be written in a single word without spaces and, if multiple tags are assigned, they must be separated by a comma “ , ”.
To assign tags to a location, do so on its “Business Details” page. To assign tags to a service, do so on its “Service Details” page as shown below.
With tags defined and assigned, they can now be used as parameters to preconfigure the widget’s content. The following examples showcase how to format tags as parameters to the “addingBookingProfile()” method.
Location Tag
In the above example, content displayed by the booking widget will be limited to stores assigned with the tag “downtown”.
<!-- Filter stores assigned with the tag “downtown” -->
<body>
<script>
var bnHandler = null;
window.bxApiInit = function ()
{
bnHandler = booxiController.configure({apiKey: "YOUR_API_KEY_HERE"});
bnHandler.addingBookingProfile('YOUR_ID_HERE',
{
apiKey: "YOUR_API_KEY_HERE",
bookingFlow: "locations",
locationTags: "downtown",
});
};
</script>
<button class="button" onclick="booxiController.open('YOUR_ID_HERE');">Downtown</button>
</body>
Service Tag
<!-- Filter services assigned with the service tag “vip” -->
<body>
<script>
var bnHandler = null;
window.bxApiInit = function ()
{
bnHandler = booxiController.configure({apiKey: "YOUR_API_KEY_HERE"});
bnHandler.addingBookingProfile('YOUR_ID_HERE',
{
apiKey: "YOUR_API_KEY_HERE",
serviceTags: "vip"
});
};
</script>
<button class="button" onclick="booxiController.open('YOUR_ID_HERE');">VIP Services</button>
</body>
In the example above, only services assigned with the tag “vip” would be displayed.
Filtering Locations Using serviceTags
<!-- Filter store locations offering at least one service with the tag “vip” -->
<body>
<script>
var bnHandler = null;
window.bxApiInit = function ()
{
bnHandler = booxiController.configure({apiKey: "YOUR_API_KEY_HERE"});
bnHandler.addingBookingProfile('YOUR_ID_HERE',
{
apiKey: "YOUR_API_KEY_HERE",
bookingFlow: "locations",
locationTags: "service:vip"
});
};
</script>
<button class="button" onclick="booxiController.open('YOUR_ID_HERE');">
VIP Services
</button>
</body>
Locations can also be filtered with the use of serviceTags. However, the tag must be formatted using the keyword “service:” followed by a valid serviceTag. In the example above, only stores offering at least one “vip” service would be listed in the widget.
Combining Tags using Logical Expressions
Service and Location tags can be combined to create complex filters using logical expressions. The property locationTags supports the “AND” operator while servicetags supports both the “AND” and “OR” operators. the To combine tags as an “AND” expression, separate each tag with a comma “ , ”; to combine tags as an “OR” expression, separate each tag with a pipe “ | ”. See below examples for proper formatting and usage. Take note that services will have to match all tags to appear as a result and only service tags support the “OR” expression.
- AND : locationTags: ‘A,B,C‘
The above will result in a match if an entry contains tag A and B and C.
- OR : serviceTags: A|B|C
The above will result in a match if an entry contains A or B or C.
- AND with OR: serviceTags: A|B , C|D
The above will result in a match if an entry contains A and B or C and D.
Multiple Tags using Logical Expressions
<!-- Filter locations assigned with the tag “downtown” and -->
<!-- offering at least one “vip” or “special” service -->
<body>
<script>
var bnHandler = null;
window.bxApiInit = function ()
{
bnHandler = booxiController.configure({apiKey: "YOUR_API_KEY_HERE"});
bnHandler.addingBookingProfile('YOUR_ID_HERE',
{
apiKey: "YOUR_API_KEY_HERE",
bookingFlow: "locations",
locationTags: "downtown",
serviceTags: "vip|special"
});
};
</script>
<button class="button" onclick="booxiController.open('YOUR_ID_HERE');">
Downtown VIP
</button>
</body>
Opening the Widget with a Service, Category or Event ID
Similarly to the bookingFlow property, it is possible to prompt the widget with a specific service, service category or event by using their ID as parameter. These IDs are automatically generated by Booxi upon creation and can be found in the “Back Office” under their respective “summary” page.
Service ID |
Service Category ID |
Event ID |
|
|
|
The following examples showcase how to format the function call and its mapping to a button element. To avoid formatting issues, pay close attention to how single and double-quotes are used when formatting parameters. Only use straight quotes.
Service ID
<!-- Prompt the booking widget with a specific service ID -->
<button onclick="booxiController.open(null,{serviceId:'YOUR_ID'});">
Book Now
</button>
Service Category ID
<!-- Prompt the booking widget with a specific service category ID -->
<button onclick="booxiController.open(null,{serviceCategoryId:'YOUR_ID'});">
Book Now
</button>
Event ID
It is possible to filter by event ID. When using the property eventCalId, it is necessary to set the bookingFlow to “locations”, provide a service ID as well as the date on which the event is scheduled.
<!-- Prompt the booking widget with a specific event ID -->
<button onclick="booxiController.open(null,{bookingFlow:'locations',
serviceId:'YOUR_SERVICE_ID',
eventCalId:'YOUR_ID',
eventDate:'YYYY-MM-DD'});">
Book Now </button>
Pushing Custom Request
The property “customRequest” can supply the booking widget with custom data. The below code showcases how to use this property. When used as a direct parameter, the content stored in customRequest will not appear in the widget but only within a booking in the Back Office.
<!-- Push custom data -->
<button onclick="booxiController.open(null,
{customRequest:'Order#:11223344 Delivery address: 123, Monterey Av.'});">
Book Now
</button>
Pushing Client Data
Pushing client data to the widget can be done by using the object “clientData”. This data will be utilized to pre-fill the client information step in the booking process. If the minimal information required is provided, the client information step can be skipped entirely by setting the property “hideClientForm” to “true”. If the minimal information isn’t provided, the client form will not be skipped. Only properties you wish to explicitly set should be defined. To push client data you must launch the widget using the “booxiController.open(...)” method as shown below.
<!-- Push custom data -->
<button onclick="booxiController.open(null,
{
clientData: {
firstname: 'YOUR_CUSTOMER_FIRSTNAME',
lastname: 'YOUR_CUSTOMER_LASTNAME',
customRequest: 'YOUR_CLIENT_CUSTOM_REQUEST',
email: 'YOUR_CUSTOMER_EMAIL',
phone: 'YOUR_CUSTOMER_PHONE',
phoneCC: '1',
emailReminder: true,
smsReminder: true,
address: 'YOUR_CUSTOMER_ADDRESS',
city: 'YOUR_CUSTOMER_CITY',
provState: 'YOUR_CUSTOMER_STATE',
country: 'YOUR_CUSTOMER_COUNTRY',
pczip: 'YOUR_CUSTOMER_ZIPCODE',
hideClientForm: true }
});">
Book Now
</button>
In the above example, data assigned to clientData would come from a database or an object defined on your website or webpage.
Hiding a Client’s Request
To prevent the content of “customRequest” from appearing in the Booking Widget, set “hideClientRequest” to “true” as shown below. Make sure the client form isn't skipped.
<!-- Push custom data -->
<button onclick="booxiController.open(null,
{
hideClientRequest: true,
clientData: {
firstname: 'YOUR_CUSTOMER_FIRSTNAME',
lastname: 'YOUR_CUSTOMER_LASTNAME',
customRequest: 'YOUR_CLIENT_CUSTOM_REQUEST',
email: 'YOUR_CUSTOMER_EMAIL',
phone: 'YOUR_CUSTOMER_PHONE',
phoneCC: '1',
emailReminder: true,
smsReminder: true,
address: 'YOUR_CUSTOMER_ADDRESS',
city: 'YOUR_CUSTOMER_CITY',
provState: 'YOUR_CUSTOMER_STATE',
country: 'YOUR_CUSTOMER_COUNTRY',
pczip: 'YOUR_CUSTOMER_ZIPCODE',
hideClientForm: false}
});">
Book Now
</button>
Using a Custom Function
Pushing client data can also be done with the use of a custom function. The below example showcases how to create a custom function that allows booking only for users that have already logged in.
<!-- Create a custom function that validates in the user is logged in -->
<!-- If logged in, allows booking, otherwise redirect to log in page -->
<body>
<script>
function userLogInStatus()
{
return user.getStatus();
}
function validateSession()
{
if(userLogInStatus())
{
booxiController.open('YOUR_ID_HERE',
{ clientData:{
customerId: user.getID(),
membershipId: user.getMembershipID(),
firstname: user.getFirstName(),
lastname: user.getLastName(),
email: user.getEmail(),
phone: user.getPhone()}
});
}
else
{
location.assign("YOUR_LOGIN_URL");
}
}
var bnHandler = null;
window.bxApiInit = function ()
{
bnHandler = booxiController.configure({apiKey: "YOUR_API_KEY_HERE"});
bnHandler.addingBookingProfile('YOUR_ID_HERE', {apiKey: "YOUR_API_KEY_HERE"});
};
</script>
<button class="button" onclick="validateSession();"> Book Now </button>
</body>
The above code is provided as an example. Your webpage’s code will most likely differ.
Callback Functions
Callback Priority
Please take note that callback functions and redirections are subject to the following priorities:
Call Priority | Function |
First | onClosed |
Second | redirectUriBooked/redirectUriClosed |
onClosed
This function is triggered when the booking widget is closed. An argument named booking data is passed to the callback and will contain booking data at the time of closure. It is therefore crucial to validate the content of “bookingSuccessful” which will only be true if the booking has been completed successfully. If the booking was aborted, checking the content of variables within bookingData can provide details on when the booking process was interrupted (ex. if serviceId is empty, the booking was aborted before a service was selected, if dateTime is empty, it was aborted before selecting a date).
Example of a callback function
<script>
function cbBookComplete(bookingData)
{
if(bookingData.bookingSuccessful !== true)
{
/* Booking not completed*/
if(bookingData.bookingStatus == 'Booking Canceled')
console.log('The user canceled the booking process.');
else
console.log('A critical error caused book now to close.');
}
else
{
/* Booking Successfully Complete */
console.log(bookingData.bookingType + ' booking Successful, id : ' +
bookingData.bookingId);
}
}
</script>
Assigning the callback function to the widget
<!-- Assigning a callback function when the widget is closed -->
window.bxApiInit = function () {
bnHandler = booxiController.configure({
apiKey: "YOUR_API_KEY_HERE",
onClosed:cbBookComplete
});
Using Redirect URLs
Customers can be redirected to predefined URLs upon completing or aborting a booking. Such URLs can be defined in the code generator on the “global config” tab or assigned manually as parameters to “booxiController.configure(...)”.
Values | Definition |
redirectUriBooked | Redirect to the assigned URL once a booking is created or updated. |
redirectUriClosed | Redirect to the assigned URL when a booking is aborted. |
<!-- Assigning redirect URL for successful and aborted booking -->
window.bxApiInit = function ()
{
bnHandler = booxiController.configure(
{
apiKey: "YOUR_API_KEY_HERE",
redirectUriBooked: "https://www.booxi.com/redirect/booked.html",
redirectUriClosed: "https://www.booxi.com/redirect/aborted.html"
}
);
As usual, simply map the “booxiController.open()” method to the “onclick” event of a button.
<button onclick="booxiController.open(null);">Book Now</button>
Retrieving Booking Data
Booking data will be available upon redirecting customers to a different URL. It gets passed as the booking query parameter to the corresponding URL. The value is a JSONobject encoded as a base 64 UTF-8 string.
To retrieve booking_data follow these steps:
- Create a temporary buffer.
- Decode data from the base64 string.
- Parse the resulting string into a JSON.
Opt for one of the following code segments depending on your environment.
Using Buffer from node.js
// Assuming query is parsed
// decode query
const buffer = Buffer.from(query, "base64");
// parse decoded string to JSON object
const booking = JSON.parse(buffer.toString("utf-8"));
Using atob
// extract query from URL
const urlParams = new URLSearchParams(window.location.search);
// extract booking_data and decode to string
const decodedData = atob(urlParams.get('booking_data'));
// parse decoded string as JSON
const booking = JSON.parse(decodedData);
- Data within “booking” is now decoded and accessible.
Take note that the Booxi Booking widget will return any information the customer has entered so far for an incomplete or aborted booking. Consequently, the information will be partial. For a complete list of properties found in the booking data, consult the following document.
This concludes the overview on features and properties.
Automatically Loading Book Now on a Landing Page
Book Now can be automatically launched upon loading a webpage. This feature can be used to prompt the widget for a specific store, a set or subset of services or for a specific event. For further details on how to load the Booking Widget on a landing page, consult the following article.
Rendering in a Webview
If you intend to use the Booking Widget in a webview within a mobile application, the widget might be displayed in 2 columns (like it would be on PC) or as a single column but with very small font making its content unreadable. To avoid such problems, add the following code in the <head> tag of the web page containing the widget.
<head>
<meta name="viewport" content="width=device-width, initial-scale=1" />
</head>
Using Google Analytics 4 (GA4)
To use GA4 with Booxi, follow the steps provided in this integration guide.