Newest DOM Features

Some newest DOM features, which have not been implemented yet on browsers are available on WDOM.

ParentNode and ChildNode Interfaces

appendChild method add only one child node, but append method can append multiple nodes at once. Furthermore, strings are also available (strings are automatically converted to a Text Node).

from wdom.tag import Ul, Li

ul = Ul()
li1 = Li('item1')
li2 = Li('item2')
...

ul.appendChild(li1)
ul.appendChild(li2)
...
print(ul.html_noid)

# by append
ul2 = Ul()
ul2.append(Li('item1'), Li('item2'))
print(ul2.html_noid)

Similarly, prepend, after, and before methods are available. Additionally, remove, replaceWith, children, firstElementChild, lastElementChild, previousElementSibling, and nextElementSibling methods/properties are also available on WDOM.

Internally, these methods update view on the browser at once, so using these methods usually results in better performance.

Custom Elements

WDOM provides limited supports on custom elements (experimental).

User Defined Custom Tags

As an example, define MyElement as a custom tag <my-element>.

from wdom.tag import Div, H1, Input
from wdom.document import get_document
from wdom.server import start

class MyElement(Div):
    tag = 'my-element'  # custom tag name
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.h1 = H1('Hello, WDOM', parent=self)  
        self.input = Input(parent=self)
        self.input.addEventListener('input', self.update)

    def update(self, event):
        self.h1.textContent = event.target.value

if __name__ == '__main__':
    document = get_document()
    # Register MyElement
    document.defaultView.customElements.define('my-element', MyElement)
    # Make instance of MyElement from HTML
    document.body.insertAdjacentHTML('beforeend', '<my-element></my-element>')
    # Or, from createElement method
    my_element = document.createElement('my-element')
    document.body.appendChild(my_element)
    start()

Difference is a class variable tag = 'my-element'.

To register MyElement class as a custom tag, use document.defaultView.customElements.define() method.

Note

Formerly, document.registerElement method was used to define custom tags, but in the latest specification uses customElements.define method to reagister custom tags. WDOM supports the same method as the latest specification.

document.defaultView property returns a reference to the window object, which is same as the window object of JavaScript on browsers.

Now you can use the registered custom tag from document.createElement('my-element') or innerHTML = '<my-element></my-element>'. Both these methods make a new instance of MyElement

Extended Custom Tags

WDOM supports to extend existing tags with is attribute.

For example, to define MyButton or DefaultButton as a custom tag:

from wdom.document import get_document
from wdom.server import start
from wdom.tag import Button, Div

class MyButton(Button):
    # tag = 'button'  <- tag name is already defined in Button class
    class_ = 'btn'
    is_ = 'my-button'  # set name at is_

class DefaultButton(MyButton):
    class_ = 'btn-default'
    is_ = 'default-button'

if __name__ == '__main__':
    document = get_document()
    # Register MyElement
    document.defaultView.customElements.define('my-button', MyButton, {'extends': 'button'})
    document.defaultView.customElements.define('default-button', DefaultButton, {'extends': 'button'})

    # Load css and js file for bootstrap
    document.add_cssfile('https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css')
    document.add_jsfile('https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js')
    document.add_jsfile('https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js')

    div = Div(parent=document.body)
    div.innerHTML = '''
        <button is="my-button">MyButton</button>
        <button is="default-button">DefaultButton</button>
    '''.strip()
    print(isinstance(div.firstChild, MyButton))  # True
    print(isinstance(div.lastChild, DefaultButton))  # True
    start()

On the class statements, add class variable is_ and specify the name of the custom tag. Class variable tag is a tag name to be extended, but in the above example, it is already defined in Button class.

When registering the custom tag, pass the name (value of is_) at the first argument to customElements.define and pass dictionary which contains 'extends' field to specify the tag name to be extended, at the third argument.

After the new tag is registered, an HTML like <button is="my-button"> will be parsed to an instance of MyElement, and <button is="default-button"> to DefaultButton.

Caution

It’s highly recommended to register custom tags as early as possible.

If the instance was generated before registering it, it becomes different class. When the customElements.define is called and registerd the custom tag, WDOM will try to update the class of existing instances but __init__ will not be not called.

Additionally, changing is attribute of the existing instances, e.g. element.setAttribute('is', '...'), do not change its class currently.

In future, Lifecycle callback methods or silimar features will be implemented, but still it’s safer to register custom tags before instanciate it.