public function data(string $key=null, $val=null)
    {
        $doc_hash = spl_object_hash($this->document);

        if ($val !== null) { // set data for all nodes
            if (!isset(self::$node_data[$doc_hash])) {
                self::$node_data[$doc_hash] = array();
            }
            foreach ($this->getElements() as $node) {
                if (!isset(self::$node_data[$doc_hash][self::getElementId($node)])) {
                    self::$node_data[$doc_hash][self::getElementId($node)] = (object) array();
                }
                self::$node_data[$doc_hash][self::getElementId($node)]->$key = $val;
            }
            return $this;
        }

        if ($node = $this->getFirstElmNode()) { // get data for first element
            if (isset(self::$node_data[$doc_hash]) && isset(self::$node_data[$doc_hash][self::getElementId($node)])) {
                if ($key === null) {
                    return self::$node_data[$doc_hash][self::getElementId($node)];
                } elseif (isset(self::$node_data[$doc_hash][self::getElementId($node)]->$key)) {
                    return self::$node_data[$doc_hash][self::getElementId($node)]->$key;
                }
            }
            if ($key === null) { // object with all data
                $data = array();
                foreach ($node->attributes as $attr) {
                    if (strpos($attr->nodeName, 'data-') === 0) {
                        $val = $attr->nodeValue[0] === '{' ? json_decode($attr->nodeValue) : $attr->nodeValue;
                        $data[substr($attr->nodeName, 5)] = $val;
                    }
                }
                return (object) $data;
            }
            if ($data = $node->getAttribute('data-'.$key)) {
                $val = $data[0] === '{' ? json_decode($data) : $data;
                return $val;
            }
        }
    }