Updated on 14 Feb 2024
This Pro tutorial provides the steps to set up a list of posts whose titles which when clicked shows that post’s title, featured image and content in an offcanvas in Bricks builder.
We shall use AJAX to dynamically fetch the clicked post’s data and load it in the offcanvas with a loading indicator appearing initially.
Note: The steps below need you to use a child theme. While it may be possible to implement this tutorial using a plugin like WPCodeBox, that is not covered.
Also, it is much easier to implement the same using BricksExtras. See this.
Intro
Here’s the overall picture:
- We create a section-type of Bricks template called “Offcanvas Post Content”. Inside this, we place the elements that should appear for each post. Ex.: Post Title, Image (set to featured image) and Post Content.
- We create a Page having a Bricks Query Loop that pulls the latest 5 posts. Each loop item contains a Basic Text element with a data attribute = the post ID.
- User clicks on the post link.
- Our JavaScript code executes vanilla JavaScript equivalent of
ajax()function using a) the URL to the file in WordPress AJAX API responsible for handling AJAX requests and b) URL of the loading indicator image passed to it from PHP. - Request gets sent to
admin-ajax.php. admin-ajax.phplooks at the registered action and runs the function hooked to this action. Inside the function we write PHP for outputting the clicked post’s content incl. any CSS that Bricks generates for the section-template.- The response gets sent back to JavaScript and we display that on the front end.

Step 2
If you aren’t already using it, download, upload and activate Bricks child theme.
It will not affect the functionality of your site if it has been built without a child theme.
Create a directory called assets.
Inside the above, create directories having these directories: js, img
Like this:
wp-content/themes/bricks-child
— assets
—— js
—— img
Step 3
Download this “loading.gif” image and upload it to the above img directory (mirror).
Create a file named load-post.js inside the above js directory having this code:
document.addEventListener("DOMContentLoaded", function() {
const toggleLinks = document.querySelectorAll("a.brxe-toggle");
toggleLinks.forEach(function(link) {
link.addEventListener("click", function(event) {
event.preventDefault();
const xhr = new XMLHttpRequest();
xhr.open("POST", bl_ajaxobject.ajaxurl, true);
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xhr.onreadystatechange = function() {
if (xhr.readyState === 4 && xhr.status === 200) {
const response = JSON.parse(xhr.responseText);
const res = response.data;
const html = res.html || false;
if (html) {
document.getElementById("brxe-ilauje").innerHTML = html;
}
}
};
const postData = "action=bl_load_post&post_id=" + this.dataset.id;
xhr.send(postData);
document.getElementById("brxe-ilauje").innerHTML =
'<img src="' + bl_ajaxobject.loadingimage + '" />';
});
});
});
The above code is vanilla JavaScript version generated by ChatGPT of this jQuery code.
Explanation
- The code wraps an event listener around the
DOMContentLoadedevent. This event is triggered when the initial HTML document has been completely loaded and parsed. - Inside the event listener function, the code selects all the anchor elements with the class name “brxe-toggle” using the
querySelectorAllmethod. These elements are stored in thetoggleLinksvariable as a NodeList. - The code then iterates over each element in the
toggleLinksNodeList using theforEachmethod, applying a function to each element. - Within the function, another event listener is added to the current
linkelement using theaddEventListenermethod. This event listener is triggered when the anchor element is clicked. - Inside the click event listener function, the code first calls
event.preventDefault()to prevent the default action of the anchor element, which is to navigate to a new URL. - The code then creates a new instance of the
XMLHttpRequestobject, stored in thexhrvariable. This object is used to make an asynchronous HTTP request to the server. - The
openmethod of thexhrobject is called with the parameters “POST”,bl_ajaxobject.ajaxurl, andtrue. This sets up the request to be sent using the HTTP POST method to the URL specified inbl_ajaxobject.ajaxurl. Thetrueparameter indicates that the request should be asynchronous. - The
setRequestHeadermethod of thexhrobject is used to set the “Content-Type” header to “application/x-www-form-urlencoded”. This specifies the format of the data that will be sent in the request. - The
onreadystatechangeproperty of thexhrobject is set to a function that will be called whenever thereadyStateproperty changes. The function checks if thereadyStateis 4 (indicating that the response is complete) and thestatusis 200 (indicating a successful response). - Inside the
onreadystatechangefunction, the code first parses the response text received from the server usingJSON.parseand assigns it to theresponsevariable. - The code then extracts the
dataproperty from theresponseobject and assigns it to theresvariable. - The
htmlproperty is extracted from theresobject and assigned to thehtmlvariable. Ifhtmlis truthy (not false, null, undefined, 0, or an empty string), the following steps are executed. - The code retrieves an element with the ID “brxe-ilauje” using
document.getElementByIdand sets itsinnerHTMLproperty to the value of thehtmlvariable. This replaces the content of the element with the new HTML content received from the server. - The code creates a
postDatavariable by concatenating the string “action=bl_load_post&post_id=” with the value ofthis.dataset.id.thisrefers to the currentlinkelement, anddatasetis an object that contains all the custom data attributes of the element. Thepost_idvalue is passed to the server as a parameter in the POST request. - The
sendmethod of thexhrobject is called with thepostDataas the parameter. This sends the POST request to the server with the specified data. - Finally, the code sets the
innerHTMLof the element with the ID “brxe-ilauje” to a loading image, indicated by the value ofbl_ajaxobject.loadingimage. This provides a visual indication to the user that the request is being processed.
We will come back to this step in a short while and make a couple of changes.
Step 4
Add the following at the end of child theme’s functions.php:
add_action( 'wp_enqueue_scripts', 'custom_enqueue_ajax_load_post' );
/**
* Loads custom js for handling the AJAX request and response.
*/
function custom_enqueue_ajax_load_post() {
if ( bricks_is_frontend() && is_page( 'offcanvas' ) ) {
wp_enqueue_script( 'bl-loadpost', get_stylesheet_directory_uri() . '/assets/js/load-post.js', [], filemtime( get_stylesheet_directory() . '/assets/js/load-post.js' ), true );
wp_localize_script( 'bl-loadpost', 'bl_ajaxobject', [ 'ajaxurl' => admin_url( 'admin-ajax.php' ), 'loadingimage' => get_stylesheet_directory_uri() . '/assets/img/loading.gif' ] );
}
}
add_action( 'wp_ajax_nopriv_bl_load_post', 'bl_load_post' );
add_action( 'wp_ajax_bl_load_post', 'bl_load_post' );
/**
* Outputs the content for the specified post.
*/
function bl_load_post() {
$args = array(
'posts_per_page' => '1',
'no_found_rows' => true,
// set post id to the ID of the post whose title has been clicked via `load-post.js`.
'p' => intval( $_POST['post_id'] ),
'post_type' => get_post_type( $_POST['post_id'] ),
);
$loop = new WP_Query( $args );
// sets up the post, sets the `in the loop` property to true.
$loop->the_post();
// content
$html = do_shortcode( '[bricks_template id="72"]' );
$additional_data = BricksElement_Template::get_builder_call_additional_data( 72 );
$styles = isset($additional_data['css']) ? $additional_data['css'] : '';
$global_classes_css = BricksAssets::generate_global_classes();
if ( $global_classes_css ) {
$styles .= "n/* GLOBAL CLASSES CSS */n" . $global_classes_css;
}
$page_css = BricksAssets::generate_inline_css_page_settings();
if ( $page_css ) {
$styles .= "n/* PAGE CSS */n" . $page_css;
}
if ( ! empty( $styles ) ) {
$html .= '<style>' . $styles . '</style>';
}
// restores original post data.
wp_reset_postdata();
wp_send_json_success( [
'html' => $html,
] );
// prevents echoing out `0` via the die function in admin-ajax.php, in addition to the above output.
die();
}
We will come back to this step in a short while and make a couple of changes.
Step 5
Go to Bricks → Templates and import this “Offcanvas Post Content” template (mirror).

Note the ID of this template.
Replace both instances of 72 in Step 4 with this ID.
Step 6
Install Zippy and import this “Offcanvas” Page (mirror).

Replace both instances of ilauje in brxe-ilauje from Step 3 with the Bricks ID of the Basic Text element in Offcanvas → Content. It is likely that ID remains the same when you imported the Page via Zippy.
That’s it!
Check the front end. If you have implemented all the steps correctly, it should work as you expect.
If you keep seeing the “Add your offcanvas content in here” text in the offcanvas, ensure that “Add Element ID & class as needed” setting is disabled in Bricks → Settings → Performance. If you want it to work with this setting enabled, set an ID for this in the ‘Offcanvas’ page and replace both instances of brxe-ilauje in the .js file with it.
If you need any help or anything is not clear, please feel free to reach out to us via the comments section or /support.
Note on Interactive Elements
Update – Note that with AJAX, any elements that use JS won’t function due to AJAX only fetching the HTML. It’s a method best used for text, images, buttons, regular HTML content. If using more interactive Bricks’ elements, you will likely need to run the JS function when the content is fetched (and some may not be able to be used via AJAX at all).
For eg, if including Bricks’ nestable accordion element and nestable tabs element..
In the javascript above, replace..
if (html) {
document.getElementById("brxe-ilauje").innerHTML = html;
}
with..
if (html) {
document.getElementById("brxe-ilauje").innerHTML = html;
bricksAccordion();
bricksTabs();
}
This will run the specific function for that element after the HTML has been added to the page
All these functions for Bricks elements are all found at the very bottom of the bricks.min.js file that is loaded on every page by Bricks.