2. Dom

Overview

When we refer to DomKit, we implicitly include the low-level dom pod. And while this pod is a stand-alone API and can be used independently of DomKit, it provides most of the heavy lifting for DomKit.

The Dom APIs generally track the W3C DOM specification verbatim so we can reuse existing knowlege and terminology. But also so we avoid creating conflicts down the line as new features are added by browser vendors.

This chapter will give a brief overview of the major dom API features. Some of the more advanced topics such as Drag and Drop and Animation will be covered in subsequent chapters

Win

The Win class maps to the W3C Window object. Most API calls will stem from the current Win instance:

// get the current Win instance for this Window
win := Win.cur

Some sample APIs available on Win:

win.alert("Hello!")  // display a modal dialog
win.uri              // the URI for this page
win.hyperlink(uri)   // hyperlink to new page
win.hisBack          // go to previous page in session history
win.viewport         // get size of window viewport

// timers
win.setTimeout(10sec) { ... }        // invoke func in 10sec
id := win.setInterval(1sec) { ... }  // invoke func every 1sec
win.clearInterval(id)                // cancel timer with id

See Win for complete Win API.

Doc

The Doc class maps to the W3C Document object.

// get the Doc for the current Win instance
doc := Win.cur.doc

Some sample APIs available on Doc:

doc.elemById("someId")            // return the Elem with id='someId'
doc.createElem("div")             // create a new <div> element
doc.querySelector("div.foo")      // find first <div> element where class='foo'
doc.querySelectorAll("div.bar")   // find all <div> elements where class='bar'

See Doc for complete Doc API.

Elem

The Elem class maps to the W3C Element object.

// create Elems
Elem("div")   // create new unattached <div> element
Elem("img")   // create new unattached <img> element

// add Elem to parent
p := Elem("p") { it.text="Lorem ipsum" }
parent.add(p)

Attributes

Elem attribute access APIs:

elem["alt"]                 // get an attr value
elem["alt"] = "Alt text"    // set an attr value
elem.attr("alt")            // get an attr (equivalent to elem["alt"])
elem.setAttr("alt", "...")  // set an attr (equivalent to elem["alt"] = "...")
elem.attrs                  // map of all defined Elem attributes

elem.tagName                // tag name of this element
elem.id                     // 'id' attribute
elem.enabled                // 'enabled' attribute
elem.text = "Foo"           // set innerText content of this element
elem.html = "<b>Foo</b>"    // set innerHTML content of this element

Properties

Elem property APIs access the JavaScript properties on the backing DOM object instance:

elem.prop("tabIndex")           // get a prop
elem.setProp("tabIndex", true)  // set a prop

Note that unlike attributes, property names are case-sensitive.

FFI

The trap operator can be used as a convenience to prop and setProp:

elem->tabIndex          // get a prop
elem->tabIndex = true   // set a prop

See Elem.trap for details when using a non-HTML namespace.

The invoke method can be used to be used to invoke JavaScript functions not exposed by the Elem API:

a.invoke("click")

Tree Operations

Methods for querying and modifying the DOM tree:

elem.parent                   // parent element
elem.prevSibling              // prev sibling
elem.nextSibling              // next sibling
elem.children                 // List of child elements
elem.firstChild               // first child, or null if no children
elem.lastChild                // last child, or null if no children
elem.containsChild(child)     // is child a descendant of parent
elem.add(child)               // add a new child element
elem.replace(cur, newChild)   // replace a child with new element
elem.remove(child)            // remove a child element
elem.querySelector("img")     // find first <img> descendant
elem.querySelectorAll("img")  // find all <img> descendants

See Elem for complete Elem API.

Style

Style provides access to both inline and class CSS styling, and is accessed with the style method:

style := elem.style

Classes

Methods for working with CSS style classes:

style.classes                // return the current class name(s)
style.hasClass("alpha")      // does elem have the given class name?
style.addClass("beta")       // add a new class to current class list
style.removeClass("gamma")   // remove a class, leaving others remaining
style.toggleClass("beta")    // add class if missing, remove if exists

Properties

Methods for working with CSS properties:

style["background-color"] = "#f00"  // set style background-color: #f00
style->backgroundColor = "#f00"     // set style background-color: #f00
style["color"]                      // get color property value
style->color                        // get color property value
style.computed("color")             // get the computed color property value

style.setAll(["color":"#f00", "background:"#eee"])  // set list of properties
style.setCss("color: #f00; background: #eee")       // set with CSS grammar

Note in the examples above the trap operator can be used as a convenience to get and set. Hyphenated properties should be specified using camel case when using trap:

style->backgroundColor == style["background-color"]

Psuedo-Classes

The addPseudoClass method can be used to define CSS pseudo classes at runtime:

style.addPseudoClass(":hover", "background: #eee")

Vendor Prefixes

Style will automatically handle adding appropriate vendor prefixes, and therefore should not be specified in implementors code.

See Style for complete Style API.

Events

DOM user events are modeled with Event. Event callbacks can be added to Win using Win.onEvent:

win.onEvent("hashchange", false) |e| { Win.cur.alert("hashchanged!") }

Likewise Elem can register callbacks using Elem.onEvent:

elem.onEvent("mousedown", false) |e| { echo("Pressed $e.target" }

To remove a registered event handler, pass the function instance to removeEvent:

func := elem.onEvent("mousedown", false) { ... }
elem.removeEvent("mousedown", false, func)

See Win, Elem and Event for complete API details.

HttpReq

The HttpReq object is used to make background HTTP requests from the browser using XmlHttpRequest. For both sync and async requests, the response is passed to you in the callback closure:

req := HttpReq { uri=`/foo` }
req.send("POST", "some content") |res|
{
  Win.cur.alert(res.content)
}

Convenience methods are available for the common request methods:

HttpReq { uri=`/foo` }.get |res| {...}
HttpReq { uri=`/foo` }.post("some content") |res| {...}
HttpReq { uri=`/foo` }.postForm(["name":"Barney Stinson"]) |res| {...}

The postForm method will automatically encode the request to look like a normal HTML form submission.

Example setting request headers:

req := HttpReq {}
req.uri = `/foo`
req.headers["Content-Type"] = "text/csv; charset=utf-8"
req.post("a,b,c") |res| { ... }

See HttpReq and HttpRes for complete API.

Web Storage

The Win API provides methods for accessing both session and local browser storage:

// local
win.localStorage["bar"]           // return value for bar from local storage
win.localStorage["foo"] = 25      // store foo:25 in local storage
win.localStorage.remove("foo")    // remove foo from local storage
win.localStorage.clear            // clear all contents from local storage

// session
win.sessionStorage["bar"]         // return value for bar from session storage
win.sessionStorage["foo"] = 33    // store foo:33 in session storage
win.sessionStorage.remove("foo")  // remove foo from session storage
win.sessionStorage.clear          // clear all contents from session storage

See Storage for complete API.

MutationObserver

MutationObserver provides access to the W3C Mutation Event API. To use a MutationObserver first create an instance and provide the callback function to invoke when a mutation occurs:

observer := MutationObserver() |recs|
{
  // callback when DOM mutation occurs where
  // recs is the list of mutations
}

Each mutation is modeled with MutationRec. Then attach your observer to a DOM node to start listening for events:

// listen for child add/remove events on elem
observer.observe(elem, ["childList":true])

If you wish to stop receiving all events for your observer instance, call disconnect

// detach this observer instance from all mutation events
observer.disconnect