Modifying the DOM
In the first two tutorials we used no input methods and only printing to the console as the output method.
Now we will explore a more powerful interface: accessing and modifying the DOM.
Accessing the DOM
The client namespace
Browser APIs are declared inside headers which are provided with Cheerp, namely
#include <cheerp/client.h> //Misc client side stuff#include <cheerp/clientlib.h> //Complete DOM/HTML5 interface#include <cheerp/webgl.h> //WebGL interface
All classes, global variables and methods exposed by the browser are declared into the client
namespace. It’s a regular C++ namespace, so it’s always possible to write more terse code with:
using namespace client;
The document object
You can access the document
global object directly from C++ code. In the next example we will add an event handler to run our code after the DOM is fully loaded. (dom.cpp)
#include <cheerp/client.h> //Misc client side stuff#include <cheerp/clientlib.h> //Complete DOM/HTML5 interface
[[cheerp::genericjs]]void outputNumberOfElementsToTheConsole(){ double number=client::document.getElementsByTagName("*")->get_length(); client::console.log("Live elements = ", number);}
//This function will be called only after the DOM is fully loaded[[cheerp::genericjs]]void loadCallback(){ client::console.log("Hi from loadCallback!");
//Do something with the document outputNumberOfElementsToTheConsole(); client::console.log("Bye from loadCallback!");}
[[cheerp::genericjs]]void webMain(){ client::console.log("Hi from webMain!"); outputNumberOfElementsToTheConsole();
client::document.addEventListener("DOMContentLoaded",cheerp::Callback(loadCallback)); client::console.log("Bye from webMain!");}
Compiling with:
/opt/cheerp/bin/clang++ -O3 dom.cpp -o dom.js
Now we need a html file:
<!doctype html><html lang="en"> <head> <meta charset="utf-8" /> <title>Cheerp example 2: DOM manipulation</title> <script src="dom.js"></script> </head> <body> <img src="/assets/Diagram_browser.png" /> </body></html>
And opening it (eg. firefox dom.html
) and checking the console log, the output should be something like:
Hi from webMain!Live elements = 5Bye from webMain!Hi from loadCallback!Live elements = 7Bye from loadCallback!
There are two important things to notice:
- the program outlived
webMain()
(while a normal C++ program terminates when themain
returns) (why?) - running code directly from inside
webMain()
can lead to race conditions, where depending on the execution order we may perform invalid operations (since the DOM is not fully formed). The pattern of invoking a callback onDOMContentLoaded
is very important and will be used also in all the following examples, and so it is the more general pattern of usingcheerp::Callback
, that we will now examine.
The cheerp::Callback
adapter function
Callback
is a function defined in the cheerp
namespace which is required to use C++ functions, functors and lambads as callbacks for browser events. For example.
cheerp::Callback(regularCXXFunc);cheerp::Callback(CXXFunctor());cheerp::Callback([](client::Event*){ ... });cheerp::Callback([capturedVariable](client::Event*){ ... });
The general usage is:
element->addEventListener("event_kind", cheerp::Callback(callback));
cheerp::Callbacks are a very powerful instrument:
- they can be attached to any pair of DOM element +
client::Event
to capture mouse movements, clicks, change of focus, button clicks, keyboard buttons pressed, etc.
- Callbacks can be attached also to
requestAnimationFrame
orsetTimeout
- Callback could be attached to
DOMContentLoaded
or equivalent events to do initializations (for example, of other callbacks)
Manipulating the DOM
Cheerp works at the same level as JavaScript. It is designed to complement or replace JavaScript as a programming language for the Web. It does not attempt to replace other Web technologies such as HTML and CSS. When using Cheerp you still have to design your page using HTML, CSS and the tools you already know.
If you want to manipulate the DOM at run-time you can use the same APIs you would use when writing JavaScript. In the following example we will create two DOM elements and set up event handling using the DOM APIs exposed by the browser.
[[cheerp::genericjs]]void setupInputAndDisplay(){ using namespace client;
//Retrieve the <body> element client::HTMLElement * body = client::document.get_body();
//Create a text input element <input type="text"> HTMLInputElement * inputBox = static_cast<HTMLInputElement*>(document.createElement("input") ); inputBox->setAttribute("type", "text");
//This sets the default value inputBox->setAttribute("value", "Insert anything in here");
//We can also style it here, but CSS would be better inputBox->setAttribute("style", "width:200px");
//Create a new <h1> element HTMLElement * textDisplay = document.createElement("h1");
//use a C++11 lambda to capture the variables we need auto mirrorText = [textDisplay, inputBox]() -> void { //Update the <h1> element with whatever is written in the <input> element String * text = inputBox->get_value(); textDisplay->set_textContent( text ); };
//Call the lambda to set the textDisplay to the initial value mirrorText();
//Set up the handler for the input event. Use a C++11 lambda to capture the variables we need inputBox->addEventListener("input", cheerp::Callback(mirrorText));
//Add the new elements to the <body> body->appendChild( textDisplay ); body->appendChild( inputBox );}
Now placing a call to setupInputAndDisplay()
from inside loadCallback()
will add the two elements to the body.
(feel free to modify the previous dom.cpp
. The dom.html
file will remain the same, link)
Even more cheerp::Callback
: buttons & mouse
buttons.cpp is an example of various uses for the cheerp::Callback
to generate buttons, text elements, check mouse position, etc. Compile it to JavaScript, keep using the same dom.html
as before, click on a few buttons.
Recap
We saw how to work with callbacks, a very general instrument that can be used to capture any kind of event.
You can now start experimenting on your own or follow our other tutorials.