Webstrates A research prototype enabling collaborative editing of websites through DOM manipulations.

The user may subscribe to certain events triggered by Webstrates using webstrate.on(event, function) (the webstrate instance) or DOMElement.webstrate.on(event, function) (the webstrate object attached to every DOM element). When an event occurs, the attached function will be triggered with potential arguments.

Trigger event when a webstrate has finished loading

When the Webstrates client has finished loading a webstrate, it will trigger a loaded event on the Webstrate instance. Using the default client.html and client.js, the webstrate instance will be attached to the window element as window.webstrate. Thus, a user may attach events to webstrate.on:

webstrate.on("loaded", function(webstrateId, clientId, user) {
  // The Webstrates client has now finished loading.
});

If a webstrate has been transcluded (i.e. loaded in an iframe), a transcluded event will be triggered, both within the transcluded iframe, but also on the iframe element itself:

var myIframe = document.createElement("iframe");
myIframe.src = "/some_webstrate";
myIframe.webstrate.on("transcluded", function(webstrateId, clientId, user) {
  // The webstrate client in the iframe has now finished loading.
});
document.body.appendChild(myIframe);

If the client is logged in using a passport provider (like GitHub), user will be an object containinig a userId, username, provider and displayName. This object is also available on the global webstrate instance as webstrate.user.

When adding a loaded or transcluded event listeners after the events have been occured, the event listeners will trigger immediately.

Data saved event

Making changes to the DOM and then immediately navigating away or closing the window, can cause the changes to be lost.

After making changes to the DOM, the MutationObserver might not have triggered yet. Even if the MutationObserver has triggered, operations might not have been created yet. Indeed, even if the operations have been created, they might not have been sent to the server yet. It’s therefore important to wait for the entire chain of events to finish before halting execution by navigating away or closing the window.

To wait for the entire chain of events to finish, we can use webstrate.dataSaved(). webstrate.dataSaved() returns a promise that will get resolved once the server has acknowledged the changes.

See How it works in the Developer guide to learn more about the process of how DOM changes get persisted.

Using JavaScript’s await syntax, this might happen something like this:

document.body.innerHTML += "<p>Hello, world!</p>";
await webstrate.dataSaved();
window.location = "https://google.com/";

The await statement ensures that we have received acknowledgment from the server that the changes have been applied.

Events on text nodes

Webstrates does fine-grained synchronization on text nodes and attributes, however, to update a text node or attribute in the browser, the whole text is replaced. To allow more fine-grained interaction with text, Webstrates also dispatches text insertion and deletion events on text nodes and element nodes:

textNode.webstrate.on("insertText", function(position, value) {
  // Some text has just been inserted into textNode by another client.
});

textNode.webstrate.on("deleteText", function(position, value) {
  // Some text has just been deleted from textNode by another client.
});

elementNode.webstrate.on("insertText", function(position, value, attributeName) {
  // Some text has just been inserted into an attribute on elementNode by another client.
});

elementNode.webstrate.on("deleteText", function(position, value, attributeName) {
  // Some text has just been deleted from an attribute on textNode by another client.
});

Added and removed nodes

Listening for added or removed nodes can be done using nodeAdded and nodeRemoved:

parentElement.webstrate.on("nodeAdded", function(node) {
  // A DOM node was added by another client.
});

parentElement.webstrate.on("nodeRemoved", function(node) {
  // A DOM node was removed by another client.
});

Changing attributes

Attribute changes will trigger attributeChanged:

childElement.webstrate.on("attributeChanged", function(attributeName, oldValue, newValue) {
  // An attribute was changed by another client.
});

If the attribute has just been added, oldValue will be undefined. If an attribute has just been removed, newValue will be undefined. If an attribute has just been updated, oldValue and newValue will contain what you’d expect.

Prime events

DOM events come in two flavors, regular events and prime events.

The regular events (e.g. nodeAdded) only get triggered when another client is the cause of the event trigger, but prime events (e.g. nodeAdded*) get triggered regardless of who caused it.

If a user adds an element to the DOM, both the nodeAdded and nodeAdded* events will get triggered on the other clients. However, only the nodeAdded* event will get triggered on the client that inserted the DOM element.

To distinguish between event triggers caused by the user himself and other users, the local parameter can be used. The local parameter will be true if the DOM change was caused by the current client (current browser) or falsy if it originated elsewhere.

parentElement.webstrate.on("nodeAdded", function(node) {
  // A DOM node was added by another client.
});

parentElement.webstrate.on("nodeAdded*", function(node, local) {
  if (local) {
    // A DOM node was added by this client.
  } else {
    // A DOM node was added by another client.
  }
});

Full list of on events

Event Arguments Description
loaded webstrateId, clientId, user The document has loaded.
transcluded webstrateId, clientId, user The document has been transcluded and loaded.
clientJoin clientId A client joins the document.
clientPart clientId A client leaves the document.
insertText position, value [, attributeName] Text has been inserted into a text node/attribute.
insertText* position, value [, attributeName], local Text has been inserted into a text node/attribute.
deleteText position, value [, attributeName] Text has been deleted from a text node/attribute.
deleteText* position, value [, attributeName], local Text has been deleted from a text node/attribute.
nodeAdded node A node has been added to the document.
nodeAdded* node, local A node has been added to the document.
nodeRemoved node A node has been removed from the document.
nodeRemoved* node, local A node has been removed from the document.
attributeChanged attributeName, newValue, oldValue An attribute has been added/modified/deleted.
attributeChanged* attributeName, newValue, oldValue, local An attribute has been added/modified/deleted.
cookieUpdateHere key, value A “here” cookie has been added/modified.
cookieUpdateAnywhere key, value An “anywhere” cookie has been added/modified.
messageReceived message, senderId, messageId, A message has been received.
messageDeleted messageId A message has been deleted.
signal message, senderId, node A client (senderId) signals on a DOM node.
tag version, label A tag has been added to the webstrate.
untag version A tag has been removed from the webstrate.
asset asset object (version, file name, etc.) An asset has been added to the webstrate.
userPermissionsChanged newPermissions, oldPermissions The user’s document permissions has changed.
permissionsChanged newPermissions, oldPermissions The document permissions have changed (any user).
disconnect   The user has been disconnected.
reconnect   The user reconnects after having been disconnected.

Unregistering event listeners

All the events can also be unregistered using off, e.g.:

webstrate.on("loaded", function loadedFunction() {
  // Work here...
  webstrate.off("loaded", loadedFunction);
});