Javascript
Ideally you would build the javascript from the Bloembraaden admin interface, just like the templates, but this is not yet possible. You need to prepare the javascript separately and upload it as script.js
to the _site
folder of the website you are designing.
Philosophy
The Bloembraaden javascript is simple (stable) old fashioned javascript. It rarely checks whether arguments supplied or elements manipulated are correct. Thus it may throw errors (watch the console output) when used incorrectly. It is by no means pretending to be type safe.
This is because I believe that in a stable design, the supplied arguments and elements manipulated will be the correct and intended ones. If you do not know which stuff you are handling, you need to step back to see what you are doing. If you cannot find out what you are handling, your design is too complex, or you need sleep.
At the same time, Bloembraaden is very lenient with everything. Stuff mostly works even if it is not entirely up to specs. Just check your design for yourself, before handing the site to your client.
Events
Your javascript can subscribe to the following events, that are dispatched on the document by default, or a DOM element (like form
) if possible. Quirk: the events are prefixed with peatcms
, the old name of Bloembraaden.
peatcms.initialize
- at the very beginning, the startup of the SPA (single page app). This is only repeated when a user returns to your website from elsewhere (including the admin interface).peatcms.navigation_start
- when navigation is initiated by the visitorpeatcms.navigation_end
- at the end of the navigationpeatcms.progressive_rendering
- while progressive parts are renderingpeatcms.progressive_ready
- when progressive rendering is donepeatcms.document_rendering
- when the document starts renderingpeatcms.document_ready
- when the template rendering is done, but loading may be in progresspeatcms.document_complete
- when the complete document is loaded (including progressive loading)peatcms.message
- a message that is being dispatched / displayedpeatcms.form_posting
- a form that is posting, dispatched on the form itself.event.detail
also contains the form.peatcms.form_posted
- form that has been successfully posted, dispatched on the form itself.event.detail
also contains the form.peatcms.form_failed
- when posting failed,event.detail
contains the form.peatcms.account_status_changed
- when a user logs in or out.
For example, if you want to reset a form when it has been posted, use:
document.addEventListener('peatcms.form_posted', function(e) { if (e.detail && e.detail.form) e.detail.form.reset(); });
Objects
Next to the carousel, lazy loading sourceset and slideshow, described in the template part of this howto, Bloembraaden exposes a couple of useful objects, most importantly PEAT
, the cms instance, NAV
, the navigation object, as well as Address
, a users address.
Sticky columns
Instantiate a PeatStickyColumns
to have two columns behave like position: "sticky";
but nice:
- Both columns start with scrolling and only stick when the visitor has seen all content.
- By styling the columns you are in control of any animations during movement.
You need to instantiate this separately. The function accepts 3 arguments:
- The left column.
- The right column.
- The distance from top, in pixels, that should be maintained (optional, default 0).
document.addEventListener('peatcms.initialize', function() { document.addEventListener( 'peatcms.document_complete', function () { new PeatStickyColumns(document.getElementById('left-column'), document.getElementById('right-column'), 20); }); });
The columns #left-column
and #right-column
need to be next to eachother. Their position will be kept level, using top margin.
#left-column { float: left; padding: 0 20px 2rem var(--padding); width: calc(50% + 200px - var(--padding) - 20px); } #right-column { float: right; padding: 0 var(--padding) 2rem 0; width: calc(50% - 200px - var(--padding)); } #right-column, #left-column { transition: margin-top 201ms ease-out; }
The transition animation is provided to have the columns move smoothly and draw attention to the moving (supposedly you have a cta or something else that is relatively important in the smaller column).
Default template objects
The Carousel and Lazyload (sourceset) objects are customizable through the template, you should not access them with javascript.
NAV (navigation object)
window.NAV
or NAV
is the instance of the PEATCMS_navigator
object that is present right from initialization. It inherits from PEATCMS_ajax
and as such exposes all the ajax methods, that you will probably not need, but are free to checkout.
The navigator object has the following methods. Access them by calling NAV.methodName(arguments)
. This list is not exhaustive, but I try to mention the most useful methods for a designer.
go(path)
- navigate to the supplied path, detects local links automatically. Always use this if you navigate from javascript to keep the SPA (single page app) in tact. Never replace the window.location or anything, it breaks the Bloembraaden experience.currentUrlIsLastNavigated
- returns whether the current url was actively navigated to by the user.refresh(path)
- likego
, but replaces the current history entry and then renders the Element.getRoot(trailingSlash)
- if you need the root ({{root}}
in the template), supply true to make sure there’s a trailing slash.getCurrentPath
- the current path uri, without extra slashesgetCurrentUri
- the complete current uri, absolute or relative when root is missing (should not happen)getCurrentElement
- the currently displayed element.getCurrentSlug
- the slug of the current element (same asNAV.getCurrentElement().slug
).submitData(slug, data, callback)
- submit data as a json object to slug (action), and execute callback when finished. This automatically adds recaptcha (Cloudflare Turnstile or Google, when active) and the correct csrf_token.submitForm(form)
- submits the supplied form through ajax, also adds recaptcha and csrf_token, when required.
PEAT (core object)
Bloembraaden exposes useful methods for your website design on the PEAT
instantiated cms object, as well as static methods on the PEATCMS
object.
Dynamic methods
The PEAT
instance of the PEATCMS
object is available from the initialization. Call its methods like so: PEAT.methodName(argument)
.
ajaxifyDOMElements(el)
- when you are actively modifying or adding DOM elements with (potentially) links or forms, usePEAT.ajaxifyDOMElements(HTMLElement)
to integrate the chunk with the SPA, preferably before actually adding it to the DOM. Failure to do this will break the Bloembraaden experience. If you do not supplyel
, the entire body will be processed, which is probably a waste of CPU cycles.currentSlugs(el)
- processes DOMElement ‘el’ for links to the current Element, defaults to entire body.setStyleRule(selector, rule)
- sets a style for the document. For instancePEAT.setStyleRule('p.red', 'color: red')
sets the color of all paragraphs with classred
to ‘red’. Currently you can only undo such a rule by setting the value toinherit
or something else that is considered neutral.lastClicked
- returns the DOM element that was last clicked (can be body).copyToClipboard(str)
- copies the supplied string to the clipboard of the users OS. Bug: this returns true even when it failed, working on that.addEventListener(type, listener, once)
- wrapper fordocument.addEventListener
but with the option to execute only once: when you supplytrue
for once, the listener will be removed after execution. ‘Once’ only works for proprietary events (peatcms.xxx
).renderProgressive(tag, slug)
- render thetag
(the slug of an element) in the supplied slotslug
in the template. Slug is optional and defaults to tag. Use when you want to re-render something that might have changed, e.g. a shoppinglist:document.addEventListener( 'peatcms.account_status_changed', function (e) { PEAT.renderProgressive( '__shoppinglist__/cart' ); });
fadeOut(el, callback)
- fade out the supplied DOMElementel
and execute the optionalcallback
when done. Note by default the element will be removed after fade out!grabAttention(el, lowKey, callback)
- have the DOMElementel
grab attention of the visitor.lowKey
is true by default and will gently call attention to the element. Supply false if you want to grab attention vehemently. Afterwards, the optional callback will be executed.scrollIntoView(el, withMargin)
- have DOMElementel
scroll into the viewport for the visitor. Supply optionalwithMargin
to leavewithMargin
pixels room between the element and the border of the viewport.scrollToTop
- will scroll document to top. You may want this after navigating (this is not standard in Bloembraaden), for example:document.addEventListener( 'peatcms.navigation_end', function() { if (NAV.currentUrlIsLastNavigated()) PEAT.scrollToTop(); });
scrollTo(x, y, el)
- scroll supplied DOMElementel
(defaults to window) tox
,y
coordinates. Like all scrolling will be smooth when supported.
Static methods
Call these useful static methods directly on the PEATCMS
object, like such: PEATCMS.methodName(argument)
.
replace(search, replace, str)
- replace a with b in c, will replace all occurences ofsearch
withreplace
instr
.numericHashFromString(str)
- creates a signed integer from a string, used to identify content.cleanUpNumber(nr)
- returns a nice float from any number that might be corrupted by decimal rounding errors.opacityNode(node, opacity)
- fades the supplied DOMElementnode
to the suppliedopacity
(decimal value between 0 and 1).isInt(value)
- returns boolean whether suppliedvalue
is an integer.cloneStructured(obj)
- returns a deep clone of theobj
, use with caution, it’s not always straightforward.cloneShallow(obj)
- returns a shallow clone of theobj
.trim(str, chars)
- trimschars
ofstr
.chars
is optional and defaults to: space, tab, and newlines.getFormData(form)
- supply a form DOMElement and this method will return all data including attribute data of the form as an object.getFormDataAsProperties(form)
- usesgetFormData
to return the object as a properties string that you can append to a url to navigate to.getBoundingClientTop(el)
- reliably gets the position from top of document of the supplied DOMElementel
.preloadImage(src, onload)
- will preload an image with srcsrc
, and execute callbackonload
when the image is loaded.isVisible(element, part)
- will return whether DOMElementelement
is visible to the visitor, at least in part optionalpart
defaults to half of the surface. (Quirk: this method is not fully developed yet.)doSlideshow(slideshow)
- supply a DOMElement formatted like a slideshow to initialize it. This will normally be done automatically by Bloembraaden.setupCarousels
- handled by Bloembraaden template.lazyLoader
- handled by Bloembraaden template.
Messages
To display a message to the visitor, like the standard Bloembraaden messages that you can style with css, use the message
method of the PEAT
instance.
PEAT.message(message, level)
-messages
can be a string, the actual message, andlevel
is an optional level that dictates the style as well as the persistance. Possible levels are:log
,warn
,error
andinfo
. Default islog
.
log
messages disappear quickly, other messages only disappear after user action. info
is ment to be a very distinctive message but not so catastrophic as an error, and is styled as such by default.
Session
Session variables are automatically synced with the server, if you use the below methods on the PEAT
instance. They are guaranteed to be current.
PEAT.setSessionVar(name, value, callback)
- set varname
(string) to valuevalue
(can be anything) and executecallback
(optional) on success.PEAT.getSessionVar(name)
- get thevalue
of session variablename
. Will be returned in the original type (object, boolean, number, string). Returns NULL when not set.
Internal objects
Peatcms_element
- used internally.Peatcms_ajax
- used internally. NAV inherits from it.Peatcms_template
- used internally. You can however instantiate it and render objects with it. See the advanced progressive loading example below.
Progressive loading example
The following example loads a ‘Contact’ section into the DOM, only when it would be visible.
function my_client_contact(el) { let loading = false; // only one request to load everything please function load() { if (true === loading) return; loading = true; NAV.ajax( '/__action__/get_template_by_name', { admin: false, template_name: 'Contact' }, function (data) { if (data.hasOwnProperty('__html__')) { load_page(new PEATCMS_template(data)); } else { console.error('Template ‘Contact’ missing'); } }); } function load_page(template) { NAV.ajax('/Contact', {}, function (json) { el.innerHTML = template.render(json); PEAT.ajaxifyDOMElements(el); PEAT.currentSlugs(el); el.setAttribute('data-contact-loaded', '1'); }); } function handle_scroll() { el.scrollTo(0, window.scrollY - 130); } function load_when_visible() { if (el.hasAttribute('data-contact-loaded')) return; if ('none' === window.getComputedStyle(el).getPropertyValue('display')) return; load(); } window.addEventListener('resize', load_when_visible, {passive: true}); load_when_visible(); document.addEventListener('scroll', handle_scroll, {passive: true}); }
Call the above function with the DOM element you want the contact Element rendered in, for instance like this:
document.addEventListener( 'peatcms.initialize', function (e) { PEAT.addEventListener( 'peatcms.document_complete', function() { const el = document.getElementById('contact'); if (el) { my_client_contact(el); } }, true); });
This javascript could be a bit more isolated but it will work fine like this.