{"action":"opened","issue":{"url":"https://api.github.com/repos/zwhu/blog/issues/43","repository_url":"https://api.github.com/repos/zwhu/blog","labels_url":"https://api.github.com/repos/zwhu/blog/issues/43/labels{/name}","comments_url":"https://api.github.com/repos/zwhu/blog/issues/43/comments","events_url":"https://api.github.com/repos/zwhu/blog/issues/43/events","html_url":"https://github.com/zwhu/blog/issues/43","id":314424152,"number":43,"title":"vdom(3)","user":{"login":"zwhu","id":2899806,"avatar_url":"https://avatars1.githubusercontent.com/u/2899806?v=4","gravatar_id":"","url":"https://api.github.com/users/zwhu","html_url":"https://github.com/zwhu","followers_url":"https://api.github.com/users/zwhu/followers","following_url":"https://api.github.com/users/zwhu/following{/other_user}","gists_url":"https://api.github.com/users/zwhu/gists{/gist_id}","starred_url":"https://api.github.com/users/zwhu/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/zwhu/subscriptions","organizations_url":"https://api.github.com/users/zwhu/orgs","repos_url":"https://api.github.com/users/zwhu/repos","events_url":"https://api.github.com/users/zwhu/events{/privacy}","received_events_url":"https://api.github.com/users/zwhu/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":0,"created_at":"2018-04-15T15:15:27Z","updated_at":"2018-04-15T15:15:27Z","closed_at":null,"author_association":"OWNER","body":"```js\r\n// /roki/index.js\r\nconst roki = function () {\r\n\r\n    let oldvnode\r\n\r\n    return {\r\n        h,\r\n        render\r\n    }\r\n\r\n    function isString(s) {\r\n        return typeof s === 'string'\r\n    }\r\n\r\n    function h(type, props = {}, children = []) {\r\n        children = children.map((child) => {\r\n            if (isString(child)) return createTextVNode(child, void 0)\r\n            return child\r\n        })\r\n        return createVNode(type, props, props.key, children, void 0)\r\n    }\r\n\r\n    function patch(oldvnode = {}, vnode) {\r\n        let element\r\n        if (vnode.type) {\r\n            if (oldvnode && oldvnode.type === vnode.type) {\r\n                element = oldvnode.dom\r\n                updateProps(element, vnode.props, oldvnode.props)\r\n            } else {\r\n                element = createElement(vnode.type, vnode.props)\r\n            }\r\n            vnode.children && patchChildren(oldvnode.children, vnode.children, element)\r\n        } else if (vnode.text) {\r\n            if (oldvnode.text) {\r\n                element = oldvnode.dom\r\n                if (vnode.text !== oldvnode.text) element.nodeValue = vnode.text\r\n            } else {\r\n                element = createTextNode(vnode.text)\r\n            }\r\n        }\r\n        vnode.dom = element\r\n        return vnode\r\n    }\r\n\r\n    function patchChildren(oldChildren = [], children = [], parentElement) {\r\n        children.forEach((child, index) => {\r\n            let oldChild = null\r\n            if (oldChildren) oldChild = oldChildren[index]\r\n            if (oldChild) {\r\n                let newNode = patch(oldChild, child)\r\n                if (oldChild.type !== child.type) {\r\n                    replaceChild(parentElement, oldChild.dom, newNode.dom)\r\n                }\r\n            } else {\r\n                let newNode = patch(undefined, child)\r\n                appendChild(parentElement, newNode.dom)\r\n            }\r\n        })\r\n\r\n        oldChildren.slice(children.length).forEach((child) => removeChild(parentElement, child.dom))\r\n    }\r\n\r\n    function render(vnode, container) {\r\n        vnode = patch(oldvnode, vnode)\r\n        if (!oldvnode) appendChild(container, vnode.dom)\r\n        oldvnode = vnode\r\n    }\r\n\r\n    function createVNode(type, props, key, children, dom) {\r\n        return {\r\n            type,\r\n            children,\r\n            props,\r\n            dom,\r\n            key\r\n        }\r\n    }\r\n\r\n    function createTextVNode(text, dom) {\r\n        return {\r\n            text,\r\n            dom\r\n        }\r\n    }\r\n\r\n\r\n    function removeChild(element, child) {\r\n        element.removeChild(child)\r\n    }\r\n\r\n    function appendChild(element, child) {\r\n        element.appendChild(child)\r\n    }\r\n\r\n    function replaceChild(element, oldChild, newChild) {\r\n        element.replaceChild(newChild, oldChild)\r\n    }\r\n\r\n    function createElement(tag, props = {}) {\r\n        const element = document.createElement(tag)\r\n        updateProps(element, props)\r\n        return element\r\n    }\r\n\r\n    function updateProps(element, props = {}, oldProps = {}) {\r\n        Object.keys(oldProps)\r\n            .filter((key) => !key.startsWith('on'))\r\n            .forEach((key) => { element.removeAttribute(key) })\r\n\r\n        Object.keys(oldProps)\r\n            .filter((key) => key.startsWith('on'))\r\n            .forEach((key) => {\r\n                const eventType = key.toLowerCase().substring(2);\r\n                element.removeEventListener(eventType, oldProps[key]);\r\n            })\r\n\r\n        Object.keys(props)\r\n            .filter((key) => !key.startsWith('on'))\r\n            .forEach((key) => { element.setAttribute(key, props[key]) })\r\n\r\n        Object.keys(props)\r\n            .filter((key) => key.startsWith('on'))\r\n            .forEach((key) => {\r\n                const eventType = key.toLowerCase().substring(2);\r\n                element.addEventListener(eventType, props[key]);\r\n            })\r\n    }\r\n\r\n    function createTextNode(text) {\r\n        return document.createTextNode(text)\r\n    }\r\n\r\n}\r\n\r\n\r\nwindow.Roki = roki()\r\n```\r\n\r\n\r\n```html\r\n<html lang=\"en\">\r\n\r\n<head>\r\n    <meta charset=\"UTF-8\">\r\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\r\n    <meta http-equiv=\"X-UA-Compatible\" content=\"ie=edge\">\r\n    <title>Document</title>\r\n</head>\r\n\r\n<body>\r\n    <button id=\"update\">update</button>\r\n    <div id=\"root\"></div>\r\n    <script src=\"./roki/index.js\"></script>\r\n    <script>\r\n        const {\r\n            render,\r\n            h\r\n        } = Roki\r\n\r\n        let vnode = h('div', {}, [\r\n            h('span', {\r\n                style: 'font-weight:bold;',\r\n                onclick: function () {\r\n                    console.log('old span')\r\n                }\r\n            }, ['This is bold']),\r\n            ' and this is just normal text',\r\n            h('a', { href: '/foo' }, ['I\\'ll take you places!']),\r\n            h('br'),\r\n            'this will delete'\r\n        ])\r\n\r\n        let newVnode = h('div', { onclick: function () { console.log('new div') } }, [\r\n            h('span', { style: 'font-weight: normal; font-style: italic;' }, ['This is now italic type']),\r\n            ' and this is still just normal text',\r\n            h('a', { href: '/bar' }, ['I\\'ll take you places!!!']),\r\n            h('br'),\r\n        ]);\r\n\r\n\r\n        render(vnode, root)\r\n\r\n        update.addEventListener('click', () => {\r\n            console.log(222)\r\n            render(newVnode, root)\r\n        })\r\n\r\n    </script>\r\n</body>\r\n\r\n</html>\r\n\r\n```"}}
{"actor":{"display_login":"zwhu"}}