Using JavaScript and DOM APIs
Note: This documentation is for the old 0.4.0 version of A-Frame. Check out the documentation for the current 1.6.0 version
Since A-Frame is just HTML, we can control the scene and its entities using JavaScript and DOM APIs as we mostly would in ordinary web development.
Image by Ruben Mueller from The VR Jump.
Every element in the scene, even elements such as <a-box>
or <a-sky>
, are
entities (represented as <a-entity>
). A-Frame modifies the HTML element
prototype to add some extra behavior for certain DOM APIs to tailor them to
A-Frame. See the Entity API documentation for reference on most of
the APIs discussed below.
Where to Place JavaScript Code for A-Frame
Before we go over the different ways to use JavaScript and DOM APIs, we
recommend encapsulating your JavaScript code within A-Frame components.
Components modularize code, make logic and behavior visible from HTML, and
ensure that code at the correct time (e.g., after the scene and entities have
attached and initialized). As the most basic example, to register a
console.log
component:
AFRAME.registerComponent('log', { |
Then to use the component from HTML:
<a-scene log="Hello, Scene!"> |
Components encapsulate all of our code to be reusable, declarative, and shareable. Though if we’re just poking around at runtime, we can use our browser’s Developer Tools Console to run JavaScript on our scene.
Getting Entities by Querying and Traversing
The wonderful thing about the DOM as a scene graph is that the standard DOM
provides utilities for traversal, querying, finding, and selecting through
.querySelector()
and .querySelectorAll()
. Originally inspired by jQuery
selectors, we can learn about query selectors on
MDN.
Let’s run a few example query selectors. Take the scene below for example.
<html> |
With .querySelector()
If we want to grab just one element, we use .querySelector()
which returns
one element. Let’s grab the scene element:
var sceneEl = document.querySelector('a-scene'); |
Note if we were working within a component, we’d already have a reference to the scene element without needing to query. All entities have reference to their scene element:
AFRAME.registerComponent('foo', { |
If an element has an ID, we can use an ID selector (i.e., #<ID>
). Let’s grab
the red box which has an ID. Before we did a query selector on the entire
document. Here, we’ll do a query selector just within the scope of the scene.
With query selectors, we’re able to limit to scope of the query to within any
element:
var sceneEl = document.querySelector('a-scene'); |
With .querySelectorAll()
If we want to grab a group of elements, we use .querySelector()
which returns
an array of elements. We can query across element names:
console.log(sceneEl.querySelectorAll('a-box')); |
We can query for elements that have a class with a class selector (i.e.,
.<CLASS_NAME>
). Let’s grab every entity that has the clickable
class:
console.log(sceneEl.querySelectorAll('.clickable')); |
We can query for elements containing an attribute (or in this case, a
component) with an attribute selector (i.e., [<ATTRIBUTE_NAME>]
). Let’s grab
every entity that has a light:
console.log(sceneEl.querySelectorAll('[light]')); |
Looping Over Entities from .querySelectorAll()
If we grabbed a group of entities using .querySelectorAll()
, we can loop over
them with a for
loop. Let’s loop over every element in the scene with *
.
var els = sceneEl.querySelectorAll('*'); |
Modifying the A-Frame Scene Graph
With JavaScript and DOM APIs, we can dynamically add and remove entities as we would with normal HTML elements.
Creating an Entity with .createElement()
To create an entity, we can use document.createElement
. This will give us a
blank entity:
var el = document.createElement('a-entity'); |
However, this entity will not be initialized or be a part of the scene until we attach it to our scene.
Adding an Entity with .appendChild()
To add an entity to the DOM, we can use .appendChild(element)
. Specifically,
we want to add it to our scene. We grab the scene, create the entity, and
append the entity to our scene.
var sceneEl = document.createElement('a-scene'); |
Removing an Entity with .remove()
To remove an entity from the DOM and thus from the scene, we call
.remove(element)
from the parent element. If we have an entity, we have to
ask its parent (parentNode
) to remove the entity.
entityEl.parentNode.removeChild(entityEl); |
Modifying an Entity
A blank entity doesn’t do anything. We can modify the entity by adding components, configuring component properties, and removing components.
Adding a Component with .setAttribute()
To add a component, we can use .setAttribute(componentName, data)
. Let’s add
a geometry component to the entity.
entityEl.setAttribute('geometry', { |
Or adding the community physics component:
entityEl.setAttribute('dynamic-body', { |
Unlike a normal HTML .setAttribute()
, an entity’s .setAttribute()
is
improved to take a variety of types of arguments such as objects, or to be able
to update a single property of a component. Read more about
Entity.setAttribute()
.
Updating a Component with .setAttribute()
To update a component, we also use .setAttribute()
. Updating a component
takes several forms.
Updating Property of Single-Property Component
Let’s update the property of the position component, a single-property component. We can pass either an object or a string. It is slightly preferred to pass an object so A-Frame doesn’t have to parse the string.
entityEl.setAttribute('position', {x: 1, y: 2: z: -3}); |
Updating Single Property of Multi-Property Component
Let’s update a single property of the material component, a
multi-property component. We do this by providing the component name, property
name, and then property value to .setAttribute()
:
entityEl.setAttribute('material', 'color', 'red'); |
Updating Multiple Properties of a Multi-Property Component
Let’s update multiple properties at once of the light component, a
multi-property component. We do this by providing the component name and an
object of properties to .setAttribute()
. We’ll change the light’s color and
intensity but leave the type the same:
// <a-entity light="type: directional; color: #CAC; intensity: 0.5"></a-entity> |
Replacing Properties of a Multi-Property Component
Let’s replace all the properties of the geometry component, a
multi-property component. We do this by providing the component name, an object
of properties to .setAttribute()
, and a flag that specifies to clobber the
existing properties. We’ll change the light’s color and intensity but leave the
type the same:
// <a-entity geometry="primitive: cylinder; height: 4; radius: 2"></a-entity> |
Removing a Component with .removeAttribute()
To remove or detach a component from an entity, we can use
.removeAttribute(componentName)
. Let’s remove the default wasd-controls
from the camera entity:
var cameraEl = document.querySelector('[camera]'); |
Events and Event Listeners
With JavaScript and the DOM, there is an easy way for entities and components to communicate with one another: events and event listeners. Events are a way to send out a signal that other code can pick up and respond to. Read more about browser events.
Emitting an Event with .emit()
A-Frame elements provide an easy way to emit custom events with
.emit(eventName, eventDetail, bubbles)
. For example, let’s say we are
building a physics component and we want the entity to send out a signal when
it has collided with another entity:
entityEl.emit('physicscollided', {collidingEntity: anotherEntityEl}, false); |
Then other parts of the code can wait and listen on this event and run code in response. We can pass information and data through the event detail as the second argument. And we can specify whether the event bubbles, meaning that the parent entities will also emit the event. So other parts of the code can register an event listener.
Adding an Event Listener with .addEventListener()
Like with normal HTML elements, we can register an event listener with
.addEventListener(eventName, function)
. When the event the listener is
registered to is emitted, then the function will be called and handle the
event. For example, continuing from the previous example with the physics
collision event:
entityEl.addEventListener('physicscollided', function (event) { |
When the entity emits the physicscollided
event, the function will be called
with the event object. Notably in the event object, we have the event detail
which contains data and information passed through the event.
Removing an Event Listener with .removeEventListener()
Like with normal HTML elements, when we want to remove the event listener, we
can use .removeEventListener(eventName, function)
. We have to pass the same
event name and function that the listener was registered with. For example,
continuing from the previous example with the physics collision event:
// We have to define this function with a name if we later remove it. |