This article outlines how and where you can use JavaScript in your Higher Logic Vanilla (Vanilla) community and knowledge base.
📝 NOTE: The combination of your Vanilla Community and Knowledge Base will heretofore be referred to as your "site."
- The front end user interface (UI) is mostly built using a JavaScript framework known as React.
- Vanilla has a customizable theming engine and many configurable widgets for administrators to control the user experience and presentation without compromising security, performance, or stability.
As an administrator, you may still find a need to use JavaScript to enhance presentation and/or user experience. However, it is recommended that you use Vanilla's built-in widgets and theming as much as possible and avoid using custom JavaScript interventions.
⚠️ IMPORTANT: If you do choose to add JavaScript to your site, you must accept the risk that it could have unwanted consequences, and that it will be your responsibility to fix and maintain that custom code.
Where can you insert JavaScript?
There are only three places you can insert JavaScript in your Vanilla community.
1. Foundation Style Guide
Themes provide all the visual presentation to your users. Your community may be using a legacy theme or the Foundation Style Guide.
📝 NOTE: If you're using a legacy theme, refer to the Pockets section below. We also recommend that you consider upgrading to the Foundation Theme for an improved user experience and increased functionality.
The Foundation Theme uses administrator-defined Theme Variables to set properties like color, fonts, spacing, etc. to give a consistent and stable presentation that can be applied to the entire community or to individual subcommunities. It does this using an interface in the Dashboard called the Style Guide.
Like the HTML widget (more on this below), the Style Guide also injects self-contained Header and Footer documents into every page of the site and allows administrators to add CSS and JavaScript that affect those documents. Although the CSS input into the Style Guide only affects the Header and Footer, JavaScript input in the Style Guide can affect any page.
2. HTML widget
HTML widgets are available in Custom Page Layouts, located on the Dashboard > Appearance > Layouts page. They can be added anywhere on a Custom Page Layout.
Widgets are like self-contained documents that are injected into a specific place on a page. Administrator can input:
- HTML that will be output to the page,
- a CSS style sheet that will modify the appearance of the widget’s HTML only,
- JavaScript that will affect the widget’s HTML only,
- and configurations and conditions for the execution of the widget.
3. Pockets
Pockets are an addon that allow administrators to add raw HTML to various places across your Vanilla site. This includes CSS styles and JavaScript.
- Pockets only appear on certain pages of the Community and do not appear at all in the Knowledge Base.
⚠️ IMPORTANT: Although not officially deprecated, Pockets are included in fewer and fewer places in Vanilla as the software evolves. Pockets are not a reliable way to inject JavaScript into your site in the future.
The rest of this article will provide an overview of how JavaScript works in the Style Guide.
JavaScript panel in the Style Guide
The JavaScript panel is an IDE (Integrated Development Environment) to help you write cleaner, safer code. This means it has:
- code completion,
- syntax,
- warnings,
- short-cuts to jump-to functionality,
- and variable declarations.
There is also a built-in search feature with regex options.
JavaScript written in the JavaScript panel of the Style Guide is not injected directly into the page: it is compiled into a separate JavaScript document that is called into the head of the document with the URL:
/api/v2/themes/[your-current-theme-id]/assets/javascript.js
Since it is already in a JavaScript document, never put script tags directly in this document. In fact, any tags or characters that do not follow JavaScript syntax will result in errors to your JavaScript. For example, if you want to inject another remote JavaScript file, do not put it in <script src=”…”></script>
.
If you do, you will see red warning lines in the JavaScript panel to let you know that you have created syntactical errors.
✔️ TIP: For help on how to call in remote JavaScript files, refer to the Tips & Tricks section below.
Vanilla Ready Event Listener
Vanilla’s frontend is rendered using JavaScript libraries, including React. This means the document you see in your browser is a collection of independent modules that are injected into the DOM in the browser.
We do not rely on the native document. DOMContentLoaded
event because Vanilla may make changes to the DOM after this has fired. For this reason, Vanilla fires its own custom event called onVanillaReady
.
Any actions you would like to perform on your document should be passed as an event handler to onVanillaReady
as a named function:
function helloWord() {
console.log(“Hell O’World!”);
}
onVanillaReady(helloWorld);
Or as an anonymous function:
onVanillaReady(() => {
console.log(“Hell O’World!”);
});
For clarity and simplicity we will use anonymous functions for all of our examples.
You can declare onVanillaReady
as often as you need in the JavaScript panel. In fact, it is recommended that you declare onVanillaReady
for every discreet function that you want to execute because onVanillaReady
has a built in try…catch
, which means that any errors occurring in one declaration of onVanillaReady
will not affect the execution of functions in another declaration of onVanillaReady
.
Vanilla Ready Event Object
onVanillaReady
returns an object that contains useful functions that you may want to use. Listed below is a list of available functions returned by onVanillaReady
:
- apiv2();[PK1]
- currentUserHasPermission();
- getAnonymizeData();
- getCurrentLocale();
- getCurrentUser();
- getCurrentUserPermissions();
- setAnonymizeData();
- translate();
This object can be passed to your handler function like this:
onVanillaReady((e) => {
const me = e.getCurrentUser();
console.log(“Hello, it’s me:”, me.name);
});
To explore the onVanillaReady
event object, you can declare it “global” by assigning it to the window object and then play with it in a browser developer console:
onVanillaReady((e) => {
window.e = e;
});
Now, in a browser developer console you can access it:
var me = e.getCurrentUser();
me;
Accessing the DOM
If you wish to access elements in the DOM, you can use native document functions such as document.getElementById()
, document.querySelector()
, document.querySelectorAll()
, document.getElementsByTagName()
, etc.
There are two things to consider when accessing the DOM:
- Be prepared to use wildcard CSS Selectors. The nature of React-built components is such that names are dynamically generated. This means that parts of CSS classes assigned to HTML elements are frequently random strings that will change whenever the JavaScript on a site is recompiled or based on the context. CSS classes are not reliable.
Here is an example of a class: css-fiknsd-Banner-styles-bannerContainer
. The portion fiknsd
is guaranteed to change the next time the code is deployed. You can access this element by using a wildcard: ` var hopefullyTheBanner = document.querySelector('div[class*="-Banner-styles-bannerContainer"]');
.
⚠️ IMPORTANT: Vanilla reserves the right to change the name of that CSS class at any time in the future, so it is recommended that if you write scripts that rely on accessing these elements that you build in contingencies that they die gracefully and verify that they work whenever there is a new code deploy.
- Consider using the window load event. Some elements are added to the DOM after
onVanillaReady
fires. If, when you try to select an element after the onVanillaReady
event fires you do not find the element, try using the window load event: window.addEventListener(‘load’, () => { document.querySelector(‘.someElement’)});
Tips & Tricks
Add JavaScript to a specific page
Sometimes you only want to execute something on certain pages (e.g., the Profile page). You can make conditional statements based on the path or other attributes to a page.
For example, let’s say you want to add code only to the Discussions page. The "page" in JavaScript is the "pathname." To get the pathname, you call window.location.pathname
. If you are on the Discussions page, window.location.pathname
will give you the string /discussions
. But if you are on a specific Discussion it will return /discussion/6/how-to-write-javascript
. To make sure that your script applies to pages that begins with /discussions
or /discussion-
, do the following:
onVanillaReady(() => {
// I just want this to appear on Discussion pages.
let pathname = window.location.pathname;
if (pathname.startsWith('/discussion')) {
// Your code goes here.
}
});
If you want it to apply only to the homepage, don’t use .startsWith()
, use ==
, which means matches exactly:
onVanillaReady(() => {
// I just want this to appear on the home. The page whose path is /.
let pathname = window.location.pathname;
if (pathname == '/') {
// Your code goes here.
}
});
You can do negative conditionals, too. Just add a !
in front of the condition. Have something appear on every page except discussions:
onVanillaReady(() => {
// I want this to NOT appear on Discussion pages.
let pathname = window.location.pathname;
if (!pathname.startsWith('/discussion')) {
// Your code goes here.
}
});
Multiple conditions by adding &&
between the two conditions. Have something appear on every page except discussions and categories:
onVanillaReady(() => {
// I want this to NOT appear on discussions AND categories pages.
let pathname = window.location.pathname;
if (!pathname.startsWith('/discussion') && !pathname.startsWith('/categories')) {
// Your code goes here.
}
});
Add CSS Styles
This function can be used to put CSS styles directly into the page when you want to cheat and customize something on the page or something you have added to the page using JavaScript.
✔️ TIP: If you don’t know how to write CSS, checkout this tutorial for an introduction to the basics.
/**
* Add style tag to the head.
*
* @params styles String Valid CSS styles.
*/
const injectCSS = (styles) => {
const head = document.querySelector('head');
const styleTag = document.createElement('style');
styleTag.innerHTML = (styles) ? styles : '';
head.appendChild(styleTag);
}
Usage
/**
* Add style tag to the head.
*/
constinjectCSS = (styles) => {
const head = document.querySelector('head');
const styleTag = document.createElement('style');
styleTag.innerHTML = (styles) ? styles :'';
head.appendChild(styleTag);
}
// The style I want to inject
constMY_OWN_STYLE=`
.Frame {
border: solid 20px #fff;
}
`
onVanillaReady((e) => {
//Put a huge white space around the Frame section.
injectCSS(MY_OWN_STYLE);
});
⚠️ IMPORTANT: Notice const MY_OWN_STYLE
wraps the CSS not in quotes ('
or "
) but in back ticks (`
). That is how you pass a variable with multiple lines in JavaScript.
Add a CSS File
This function can be used to call in a CSS file from a remote location. Some of the things that can go on would be CORs issues. The domain of the URL that you pass to this should be whitelisted in the Security Section of the Vanilla Dashboard.
/**
* Add CSS file into the head.
*
* @params validUrl The URL to the CSS file to be included.
*/
const injectCSSFile = (validUrl, rel) => {
const head = document.querySelector('head');
const styleTag = document.createElement('link');
styleTag.setAttribute('href', validUrl);
styleTag.setAttribute('rel', rel);
styleTag.setAttribute('type', 'text/css');
head.appendChild(styleTag);
}
Add some in-line JavaScript
Inject some JavaScript into the head of the page.
Usage
/**
* Add JS into the head.
*
* @params script String Valid JavaScript scripts.
*/
const injectJS = (script) => {
const head = document.querySelector('head');
const scriptTag = document.createElement('script');
scriptTag.innerHTML = (script)? script :'';
head.appendChild(scriptTag);
}
onVanillaReady(()=>{
const js ='console.log("Hello World");';
injectJS(js);
});
Injects a piece of JavaScript that will then write to the console “Hello World.”
Add a JavaScript file
Injects a script tag into the head that calls has src tag to call an entire JavaScript file from some exterior source. Takes a parameter of a valid URL.
⚠️ IMPORTANT: The domain of the URL must be registered in the Security section of the Vanilla Dashboard. Even if it is registered there, it might still not work depending on the configuration of the server where it is hosted.
Usage
/**
* Add JS into the head.
*
* @params validUrl The URL to the JavaScript file to be included.
*/
constinjectJSFile = (validUrl) => {
const head = document.querySelector('head');
const scriptTag = document.createElement('script');
scriptTag.src = (validUrl)? validUrl :'';
head.appendChild(scriptTag);
}
constGTM="https://www.googletagmanager.com/gtag/js?id=UA-xxxxxx-xx";
onVanillaReady(()=>{
injectJSFile(GTM);
});
Cookies
The following functions read, write, and delete cookies:
/**
* Find out if a cookie exists
*
* @returns bool
*
**/
const cookieExists = (name) => {
return document.cookie.split(';') .some(c => {
return c.trim() .startsWith(name +'=');
});
};
/**
* Get the value of a given cookie.
*
* @returns mixed false or string;
*
**/
const getCookieValue = (name) => {
let cookie = document.cookie.split(';').find(c => {
return c.trim().startsWith(name + '=');
});
if (cookie !==undefined&& cookie){
return cookie.split('=')[1];
}
return false;
};
/**
* Set a cookie for a number of days.
*
**/
const setCookie = (name, value, domain, days) => {
var expires ="";
if(days){
var date =newDate();
date.setTime(date.getTime()+(days*24*60*60*1000));
expires ="; expires="+ date.toUTCString();
}
document.cookie= name + "=" +(value || "") + expires + "; path=/"+((domain)?";domain="+domain:"");
};
/**
* Expire a cookie by changing its date to the past.
* This turns it into a session cookie. If the user does not close browser
* the cookie will remain active.
*
**/
constexpireCookie = (name, value, domain) => {
if ( getCookie( name ) ) {
setCookie(name, value, domain,'Thu, 01 Jan 1970 00:00:01 GMT')
}
}
Vanilla JavaScript best practices
Every JavaScript intervention carries with it a small risk of breaking the presentation or functionality of a page. To minimize the risk:
- Pay attention to errors and warnings in the JavaScript panel of the Style Guide.
- Inspect your code in the browser’s developers console and correct any errors and warnings caused by your JavaScript.
- Follow generally accepted JavaScript best practices.
- Add document blocks to your code to help others troubleshoot and maintain.
- Break up your features into distinct declarations of
onVanillaReady
.