Element

Element

Elements represent the adaptation of the HTML Document Object Model to Croquet. While the basic API is modeled after the DOM spec, it has Croquet-specific features. On the other hand, it omits some types of elements in the standard.

Elements are created and appended to the "top" element to form a tree. For each Element model object, a corresponding ElementView object is created. Each ElementView object instantiates the corresponding native DOM element. When some code updates a property on an Element model, the framework applies the change to the native DOM element. The View side code can manipulate the View object as well as the native DOM element.

A virtual DOM element holds its parent element (parentNode), the list of children (childNodes), the dictionary of style properties (style), its DOM ID (renamed to domId to avoid name conflict with Croquet model id), CSS class list (classList), and the static part of the sub-element in HTML form (innerHTML).

It also stores a set of expander code blocks for the model side of the Element, and another set for its View counterpart. An Element also contains a string representation of its CSS class definitions.

A subset of the commonly used DOM API is implemented for Element. For instance, methods such as appendChild() and removeChild() can be used to set up the display scene.

The set of expanders for Element (code) or ElementView (viewCode) are converted to an executable form. Typically an expander has an API call to add an event listener (addEventListener()) or a Croquet event subscription (subscribe()) to trigger a method in it.

Element has several subclasses for custom DOM element types. There are subclasses for iframe, canvas, video, img, and textarea. The first four are relatively simple wrappers to handle specific features. The textarea creates a multi-user collaborative text editor. Other element types (such as ul, select, h1, etc.) are not included, as a div element with style and event handlers can mimic them. But when the need arises, we could add them to the framework.

Members

# (readonly) style :Style

The virtualized CSSStyleDeclaration object for the receiver. It has setProperty() getPropertyValue(), and removeProperty() (a subset of the spec described in CSSStyleDeclaration).

Type:
  • Style

# (readonly) classList :ClassList

The classList of the receiver. The classList object has add(), remove(), replace(), and contains() (a subset of the spec described in classList).

Type:
  • ClassList

# domId :string|undefined

Element's DOM ID.

Type:
  • string | undefined

Methods

# appendChild(elem)

Add a child Element to the end of an Element's childNodes list.

Parameters:
Name Type Description
elem Element | ElementRef

the child element to be added

Example
let another = this.createElement("div"); this.appendChild(another);

# insertBefore(elem, referenceElemopt)

Add a child Element in front of referenceElem in the childNodes list. If referenceElem is null, the element is added at the end of the list.

Parameters:
Name Type Attributes Description
elem Element | ElementRef

the child element to be added

referenceElem Element | ElementRef <optional>

the child element to be added

Example
let anchor = this.querySelector("#myElem"); this.insertBefore(another, anchor);

# remove()

Remove the receiver from its parent's childNodes. If the receiver is not in the display scene, this method does not have any effect.

Example
another.remove();

# removeChild(elemopt)

Remove the specified Element from the receiver's childNodes.

Parameters:
Name Type Attributes Description
elem Element | ElementRef <optional>

the child element to be removed

Example
this.removeChild(another);

# addEventListener(eventType, methodName, useCaptureopt)

Add a listener for a DOM event specified by eventType to the receiver. This resembles the native DOM's addEventListener, but the second argument is restricted to a string that specifies the name of a method, to conform to Croquet's Model. A typical case is to call addEventListener() from an expander. In that case, the method is looked up from the calling expander.

Parameters:
Name Type Attributes Description
eventType string

the DOM event type

methodName string

the name of the handler in the calling expander

useCapture boolean <optional>

indicating if the event will be dispatched to elements below

Example
this.addEventListener("click", "onClick"); // onClick is the name of a method

# removeEventListener(eventType, methodName)

Remove the specified event listener from the Element.

Parameters:
Name Type Description
eventType string

the DOM event type

methodName string

the name of the handler in the calling expander

Example
this.removeEventListener("click", "onClick");

# querySelector(query) → {Element|null}

Look up an Element that matches the specified query. This is similar to https://developer.mozilla.org/en-US/docs/Web/API/Element/querySelector but, as of writing, it only works with the #id form.

Parameters:
Name Type Description
query string

# followed by a domId

Returns:

the Element found, or null.

Type
Element | null
Example
let elem = this.querySelector("#myElement");

# createElement(elementType) → {Element}

Create a virtual DOM Element of type specified by elementType. It is similar to the native DOM API, which uses the global document.createElement (https://developer.mozilla.org/en-US/docs/Web/API/Document/createElement). elementType is one of "div", "canvas", "textarea", "iframe", "video", or "img".

Parameters:
Name Type Description
elementType string

the type element to instantiate

Returns:

An instance or sub-instance of Element

Type
Element
Example
let elem = this.createElement("canvas");

# setCode(code)

Set the receiver's list of expanders (on the model side). The argument can be either a string specifying a single expander, or an array of strings to specify multiple expanders. If a string has the dot-delimited form libraryName.expanderName, the expander code is looked up from the "library" of the receiver. Alternatively, a string can contain a full expander definition starting with the keyword class. The first form is provided to save space when there are many instances of Element that share the same expander.

Parameters:
Name Type Description
code string | Array.<string>

singleton code (a string) or an array of strings that specify expanders

Examples
this.setCode("myLib.MyExpander");
this.setCode("class C {onClick {this.remove();}}");
this.setCode(["myLib.MyExpander", "myLib.YourExpander"]);

# addCode(code)

Add a list of expanders to the receiver. The difference from setCode() is that setCode() removes all existing expanders while addCode() doesn't.

Parameters:
Name Type Description
code string | Array.<string>

singleton code (a string) or an array of strings that specify expanders

Example
this.addCode("myLib.MyExpander");

# getCode() → {string|Array.<string>}

Return the list of expanders.

Returns:
  • singleton code (a string) or an array of strings that specify expanders
Type
string | Array.<string>

# setViewCode(code)

Set the list of expanders for the receiver's View. The argument can be either a string specifying a single expander, or an array of strings to specify multiple expanders.

Parameters:
Name Type Description
code string | Array.<string>

singleton code (a string) or an array of strings that specify expanders

Examples
this.setViewCode("myLib.MyExpander");
this.setViewCode("class C {onClick {this.remove();}}");
this.setViewCode(["myLib.MyExpander", "myLib.YourExpander"]);

# addViewCode(code)

Add a list of expanders to the receiver's View. The difference from setViewCode() is that setViewCode() removes all existing expanders while addViewCode() doesn't.

Parameters:
Name Type Description
code string | Array.<string>

singleton code (a string) or an array of strings that specify expanders

Example
this.addViewCode("myLib.MyExpander");

# getViewCode() → {string|Array.<string>}

Return the list of expanders for the view

Returns:
  • singleton code (a string) or an array of strings that specifies expanders
Type
string | Array.<string>

# setStyleClasses(style)

The style string is stored in the Element, and added to the native display scene as a <style> node. Note that it is in the global scope so the CSS selectors may be picked up by other elements.

Parameters:
Name Type Description
style string

CSS classes to be included in the document

Example
this.setStyleClasses(`.foo {background-color: black}`);

# addStyleClasses(style)

Append the style to the existing styleClasses string.

Parameters:
Name Type Description
style string

CSS classes to be included in the document

Example
this.addStyleClasses(`.bar {width: 10px}`);

# getLibrary(path) → {string|class|null}

Returns the content in the Library mechanism. Typically a Library for an app is set up at the app's load time. The path is in the form libraryName.item, and the value of the specified item is returned. The return value is either a function or expander as a string, or an actual class object if specified item is in the classes array of the library.

Parameters:
Name Type Description
path string

dot-delimited path to the library in the Element and its ancestors

Returns:
Type
string | class | null
Example
this.getLibrary("myLib.MyClass");

# _set(name, value)

Store a value in the property dictionary for the receiver Element. NB: The underscore in the method name is a reminder that the value has to be a data structure that the Croquet serializer can serialize.

Parameters:
Name Type Description
name string

name of the property

value any

the new value of the property. It must be serializable by the Croquet Serializer.

Example
this._set("myData", new Map());

# _get(name) → {any}

Retrieve the value for name in the Element's property dictionary.

Parameters:
Name Type Description
name string

name of the property

Returns:

The value of the property.

Type
any
Example
this._get("myData");

# _delete(name)

Delete the entry from the Element's property dictionary.

Parameters:
Name Type Description
name string

name of the property to be deleted

Example
this._delete("myData");

# subscribe(scope, eventName, methodName)

Subscribe to a Croquet message specified by scope and eventName. The method specified by the methodName is invoked when the Croquet message is delivered to the model. Scope and eventName can be arbitrary strings. A common practice is to use the model ID (not to be confused with domId) as the scope to indicate that a message is related to a given Element, and eventName to specify the message's purpose.

Parameters:
Name Type Description
scope string

Croquet message scope

eventName string

Croquet message event name

methodName string

name of the expander method to invoke

Example
this.subscribe(this.id, "myMessage", "myMessageHandler");

# publish(scope, eventName, data)

Send a Croquet message with scope and eventName, with data as payload.

Parameters:
Name Type Description
scope string

Croquet message scope

eventName string

Croquet message event name

data any

a serializable value

Example
this.publish(this.id, "myMessage", {a: 1, b: 2});

# call(expanderName, methodName) → {any}

Invoke a method in the receiver's specified expander, with arguments. An error will be thrown if the receiver has no expander of that name, or the expander does not include that method. There is no need to use this form if the method is invoked from the same expander. In some cases, however, it is desirable to be able to invoke a method from a different expander, or a method of a different receiver.

Parameters:
Name Type Description
expanderName string

name of the expander

methodName string

name of the method

...arguments any

arguments for the method

Returns:

the return value from the method

Type
any
Example
other.call("OtherExpander", "myMethod", 1, 2, 3);