Skip to content

client dom

Frank van Viegen edited this page Sep 23, 2015 · 2 revisions

Use this module to define HTML layouts in a reactive manner: every element automatically creates a new observer scope (see obs.observe), re-rendering the subtree whenever a change occurs in an observed value. This is the only way to create DOM layouts, access to functions such as document.createElement is prohibited.

Dom = require 'dom'
Obs = require 'obs'
{tr} = require 'i18n'

counter = Obs.value(0)
Dom.div !->
	Dom.text tr("the counter is at %1", counter()) 
	Dom.style {background: 'red'}
	Dom.prop 'tabindex', 3
	Dom.onTap !->
		(counter counter()+1)

Take a look at the obs module for more observable examples and semantics.

Node and event wrapping

The dom module is never supposed to "leak" native HTMLNode or Event objects to its users, because it would allow plugins to effectively break out of their sandboxing environment (for instance, they could follow parent nodes outside the plugin container). HTML nodes and events returned by the API are therefor always wrapped by NodeWrapper and EventWrapper prototypes, respectively. Read more about the sandbox.

Functions

htmlTag (content)

Where htmlTag is a valid HTML5 tag such as div, span, input, etc.

Creates new element and append to the current scope. content should be a function. It is run every time the element needs to be re-rendered. It can define properties, event handlers, text content or sub-elements.

Returns nothing, use the get() or last() functions to get a (wrapped) reference to the element.

Example:

	Dom.section !->
		Dom = require 'dom'

		Dom.h3 "A lovely title"
		Dom.p "Some text as to demonstrate things"
		Dom.div !->
			Dom.style
				background: "#555"
				color: "#FFF"
			Dom.span "Just send a function to rescope and make children"
			Dom.small "small children"

text(text)

Create a HTML text node and append in the current scope.

Example:

	Dom = require 'dom'

	Dom.section !-> 
		Dom.text "Adding plain text."
		Dom.text "This does not add a line break."
		Dom.text "And neither does this: \n."
		Dom.text "Mind you that the <b> text is escaped </b> so HTML tags <br> come out as <span>strings</span>."

content(textFunc)

Similar to text(text), but it also accepts functions.

Example:

	Dom = require 'dom'

	Dom.section !-> 
		Dom.content "Random Avenger: "
		Dom.content ->
			a = ["Iron Man", "Captain America", "Hulk", "Thor", "Black Widow", "Hawkeye"]
			a[Math.floor(Math.random() * a.length)]

userText(text, options)

This has roughly the same functionality as text(text), but the content is parsed to add a few more features.

  • \n is replaced with a functioning <br>, so you can add newlines.
  • Asterisk can be used emphasize text.
  • Double asterisk can be used to make text strong.
  • Urls are formatted and functional

Because of this, it is useful as output for user inputted messages.

Example:

	Dom = require 'dom'

	Dom.section !-> 
		Dom.userText "For one, you can add \n newlines."
		Dom.userText "*italic* text, **bold** it, or ***both*** \n"
		Dom.userText "urls: www.xkcd.com are formatted.\n"
		Dom.userText "emoji are supported: \u23f3"

options is an object that can disable these features. Per default, they are all enabled.

	options = {
		br = false, #disable line breaks
		rich = false, #disable bold and italic styling
		urls = false, #disable URL formatting
		emoji = false, #disable emoji parsing.
	}

richText(text)

Short for userText(text, {urls:false,br:false})

brText(text)

Short for userText(text, {urls:false})

html(html)

Convert HTML to a (series of) node(s) and append in the current scope.

on(eventName, cb)

Register event callback in the current scope. Cleaned up when the scope is torn down.

viewport()

Exposed observable hash with properties height and width that indicate viewport size. Although page.width and page.height are more convenient.

get()

Returns a NodeWrapper instance of currently in-scope element.

Example:

	Dom = require 'dom'
	Obs = require 'obs'

	out = Obs.create("empty")
	Dom.section !->
		Dom.text out.get()
		Dom.input !->
			inputE = Dom.get()
			Dom.on 'change', !->
				out.set(inputE.value())

last()

Returns a NodeWrapper instance of the last-created element.

Example:

	Dom = require 'dom'
	Ui = require 'ui'

	Dom.section !->
		Dom.div !->
			Dom.h2 "An element"
			el = Dom.last() # gets the h2 element
			Ui.button "Click me", !->
				el.setText "This element is happening"

css(hash)

Add dedicated css. Similar to the usage of separate <style></style> tags. Like Dom.style, it accepts an hash which should be a javascript object with camelcased css properties as keys. Prefix a key with _ to add vendor-prefix before the css property. Postfix a key with _ to add a vendor-prefix before the css value. The usual pattern is to apply this to the current element using the Dom.style shortcut:

Example:

	Dom = require 'dom'

	Dom.section !->
		Dom.style
			height: "2em"

		Dom.div !->
			Dom.text "Somebody to..."
			Dom.span !->
				Dom.addClass 'right'
				Dom.text "Love"

	Dom.css
		'.right':
			marginTop: "1em"
			float: 'right'

on(eventNames, func)

Subscribes given function to an event associated to the current scoped element.

Example:

	Dom = require 'dom'

	Dom.section !->
		Dom.input !->
			inputE = Dom.get()
			Dom.on 'change', !->
				Modal.show "Text changed to " + inputE.value()

onTap(opts)

Makes an object interactive.

For a simple clickable:

	Dom = require 'dom'

	Dom.section !->
		Dom.div !->
			Dom.style
				color: require('plugin').colors().highlight
			Dom.text "Press me"
			Dom.onTap !->
				Modal.show "I'm tapped!"

animationFrame (func)

Similar to window.requestAnimationFrame. Only you don't get the parameter send to the callback to describe the progress. It returns an animationFrame ID.

Example:

	Dom = require 'dom'
	UI = require 'ui'

	i = 0
	el = null
	req = null

	frame = (timestamp) !->
		i++
		el.setText "count " + (i%240)
		req = Dom.animationFrame frame

	Dom.section !->
		Dom.div !->
			Dom.text "count " + 1
			el = Dom.last()
		Ui.button 'Stop', !->
			Dom.cancelAnimationFrame req

	req = Dom.animationFrame frame

cancelAnimationFrame (id)

Like window.cancelAnimationFrame(id), this stoppes an animation. Example is above at animationFrame (func)

getContext(type)

Returns the context. Used for drawing on a canvas:

Example:

	Dom = require 'dom'

	Dom.canvas !->
		ctx = Dom.getContext '2d'
		ctx.fillStyle = '#f0f'
		ctx.fillRect(20,20,60,60);

overflow()

Sets the overflow property to 'overlay'

trackTouch(cb, rootElement)

An abstraction for touch and mouse events. In the cases where 'onTap()' is not enough.

You provide Dom.trackTouch with an callback function which receives an array of touches. In addition, you also need to provide a wrapped element from which the event is based.

AS parameter it will receive an object for each touch (multi-touch supportive). Each is an object with the following members:

  • x : translated pixels from touch start
  • y : translated pixels from touch start
  • xc: translated pixels from origin of element
  • yc: translated pixels from origin of element
  • xo: start pixels from origin of element (doesn't change while dragging)
  • yo: start pixels from origin of element (doesn't change while dragging)
  • start: timestamp of the start of the thouch
  • id: touch index?
  • op: touch state, to be read as multiple flags in a byte, not as an integer:
    • touch.op&1 checks for the "touch start" state
    • touch.op&2 checks for the "touch move" state
    • touch.op&4 checks for the "touch end" state The return value of the callback function determines if the default touch behavior should be suppressed or not. Return either true or false

Draggable box example:

Dom = require 'dom'

exports.render = !->
	element = null
	Dom.div !->
		Dom.style
			width: '100px'
			height: '100px'
			backgroundColor: 'wheat'
			Box: 'center middle'
		Dom.text "Drag me"

		element = Dom.get()

	Dom.trackTouch (touch1, touch2) -> # you can also use 'touches...'
		if touch1
			color = 'wheat'
			if touch1.op&1 then color = 'DarkKhaki' # touch start
			if touch1.op&2 then color = 'DarkSalmon' # touch move
			if touch1.op&4 then color = 'DarkSeaGreen' # touch end
			log color
			x = touch1.x + touch1.xo
			y = touch1.y + touch1.yo - 50 - element.height()/2 # delta y - starty - top bar - half element's height
			element.style _transform: "translate(#{x + 'px'}, #{y + 'px'})"
			element.style backgroundColor: color
		return false # disable native scrolling
	, Dom.get()

Prototype NodeWrapper

All functions exposed in this prototype are also available directly on the dom module, operating on the current element.

NodeWrapper.style(hash)

Apply .style to the element. hash should be a javascript object with camelcased css properties as keys. Prefix a key with _ to add vendor-prefix before the css property. Postfix a key with _ to add a vendor-prefix before the css value. The usual pattern is to apply this to the current element using the Dom.style shortcut:

Dom.div ->
	Dom = require 'dom'

	Dom.style
		display_: 'box'
		_boxAlign: 'center'
	Dom.text "my box"

NodeWrapper.cls(className)

Reactively add css class.

NodeWrapper.getStyle(prop)

Get style property.

NodeWrapper.style

Set style of the wrapped element.

Example:

	Dom = require 'dom'

	Dom.section !->
		Dom.div !->
			el = Dom.get()
			el.style
				'backgroundColor': '#F00'

NodeWrapper.clearStyle

Remove all syling of the element. External CSS is left untouched.

NodeWrapper.getTransformXY()

Return {x,y} object representing actual webkit transformation.

NodeWrapper.getOffsetXY(parent=document.body)

Return offset x/y relative to parent element.

NodeWrapper.prop(propName,newValue?)

Get/set element property. Not all properties are allowed (e.g. parentNode).

NodeWrapper.value(newValue?)

Short for .prop('value', newValue)

NodeWrapper.setText(text)

This sets the innerText of the element. Example:

	Dom = require 'dom'
	UI = require 'ui'
	Plugin = require 'plugin'

	Dom.section !->
		Dom.div !->
			el = Dom.get()

			Dom.style
				color: require('plugin').colors().highlight
			Dom.text "Press me"
			Dom.onTap !->
				el.setText("Bunny!");

NodeWrapper.addClass(className)

Adds a class to selected element.

NodeWrapper.removeClass(className)

Removes a class to selected element.

Prototype EventWrapper

EventWrapper.kill(stopPropagation=false,preventDefault=false)

Applies stopPropagation and preventDefault on the event. Useful if you want to override the default behavior.

EventWrapper.getTarget()

Returns the target of the event wrapped in a NodeWrapper.

Example:

Dom = require 'dom'
Plugin = require 'plugin'

el = null
Dom.section !->
	Dom.div !->
		Dom.style
			color: require('plugin').colors().highlight
		Dom.text "Press me"
		el = Dom.get()
		Dom.onTap (event) !->
			target = event.getTarget()
			target.setText "Touched"

EventWrapper.getTouchXY(element)

Returns the X and Y position of the click/touch event relative to the given element.

Example:

Dom = require 'dom'
Plugin = require 'plugin'

el = null
Dom.section !->
	Dom.div !->
		Dom.style
			color: require('plugin').colors().highlight
		Dom.text "Press me"
		el = Dom.get()
		Dom.onTap (event) !->
			xy = event.getTouchXY el
			log xy
			el.setText("touched: (" + xy.x.toFixed(2) + "; " + xy.y.toFixed(2) + ")")

EventWrapper.getKeyCode()

Returns they keycode of the key pressed.

Example:

Dom = require 'dom'

Dov..section !->
	Dom.div !->
		Dom.text "Input:"
		Dom.input !->
			Dom.on 'keydown', (event) !->
				log event.getKeyCode()

EventWrapper.prop(key)

returns the value of one of the following keys:

  • shiftKey
  • ctrlKey
  • altKey
  • button

Example:

Dom = require 'dom'

Dov..section !->
	Dom.div !->
		Dom.text "Input:"
		Dom.input !->
			Dom.on 'keydown', (event) !->
				log event.getKeyCode()
				log event.prop('shiftKey')
				log event.prop('ctrlKey')
				log event.prop('altKey')
				log event.prop('button')

Basic topics

API reference

  • API Reference
    • Client
      • [client plugin](client plugin)
      • [client dom](client dom)
      • [client obs](client obs)
      • [client db](client db)
      • [client server](client server)
      • [client page](client page)
      • [client ui](client ui)
      • [client form](client form)
      • [client icon](client icon)
      • [client modal](client modal)
      • [client photo](client photo)
      • [client photoview](client photoview)
      • [client time](client time)
      • [client share](client share)
      • [client map](client map)
      • [client geoloc](client geoloc)
    • Server
      • [server event](server event)
      • [server plugin](server plugin)
      • [server http](server http)
      • [server db](server db)
      • [server photo](server photo)
      • [server time](server time)
  • Example UI elements

Advanced topics

Clone this wiki locally