{"id":20638901,"url":"https://github.com/merri/nom","last_synced_at":"2025-10-14T11:08:41.805Z","repository":{"id":30187176,"uuid":"33737884","full_name":"Merri/nom","owner":"Merri","description":"This is 2015 version of a DOM library idea","archived":false,"fork":false,"pushed_at":"2019-05-04T10:34:48.000Z","size":495,"stargazers_count":13,"open_issues_count":1,"forks_count":0,"subscribers_count":1,"default_branch":"gh-pages","last_synced_at":"2025-03-24T21:03:34.148Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":false,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Merri.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-04-10T16:09:45.000Z","updated_at":"2019-05-04T10:37:56.000Z","dependencies_parsed_at":"2022-08-28T18:43:49.452Z","dependency_job_id":null,"html_url":"https://github.com/Merri/nom","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Merri%2Fnom","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Merri%2Fnom/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Merri%2Fnom/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Merri%2Fnom/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Merri","download_url":"https://codeload.github.com/Merri/nom/tar.gz/refs/heads/gh-pages","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":249162041,"owners_count":21222585,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-16T15:20:35.129Z","updated_at":"2025-10-14T11:08:36.753Z","avatar_url":"https://github.com/Merri.png","language":"JavaScript","readme":"# NOTE: This is a Proof-of-Concept from 2015: [visit a newer version of this project](https://github.com/Merri/nomjs)!\n\nThe new version of NomJS steals React's JSX syntax for ease-of-use and works from there while attempting to maintain the core idea of embracing native DOM elements. As such new NomJS will drop a lot of legacy support and aim for the future. This is totally different from this original idea of the 2015 version, which could run in browsers as old as IE5 and have no dependencies, being very much browser only.\n\n---\n\n\u003e Nom (or nom.js or NomJS) is a new DOM library. It has been inspired by both [React](http://facebook.github.io/react/) and [Riot 2](https://muut.com/riotjs/), but Nom only does DOM, and does it only in browser. Thus Nom focuses on doing just one thing right instead of becoming more of a framework. Nom gives you no help to making isomorphic sites, tells you in no way how you should go with your routing (standalone library or custom solution can do it better anyway) and doesn't practically force you into using build tools. Nom won't make you happy if you're following the trends!\n\n## 1.5 kB DOM library • browser only • compact syntax\n\n1. Nom creates DOM elements.\n2. Nom helps you keep your data synchronized with DOM elements.\n3. Nom makes it easy to create DOM trees.\n\nYou can think of Nom as a kitten. Or just a kitten's head. It makes your code look cute. Your app will be the body. And then your code will produce... rainbows. Because that is how cats work on the Internet.\n\n## Nom is...\n\n1. Close to the standards: Nom only outputs DOM nodes.\n2. Minimal like Riot, but with even smaller focus.\n3. Performant. Nom's source prefers inline pattern repetition over helper functions.\n4. Compatible. You can use other tools to screw the DOM and Nom will adapt.\n5. Small like a kitten. Makes even your non-iOS mobile phone *purrrr*.\n6. Easy for anyone to just drop on their site (like jQuery) and it just works.\n7. Ideal for beginners: Nom takes the hard away and doesn't rename things for sake of \"convenience\".\n\nSo Nom is... **absolutely brilliant**! (I'm trying to be convincing here. Play along.)\n\n## Documentation\n\nNom comes with three methods: `el`, `els` and `mount`.\n\n1. `el` returns a single DOM element.\n2. `els` returns a document fragment possibly containing multiple DOM elements.\n3. `mount` returns a document fragment which has been assigned to automatic updates.\n\n### Creating an element\n\n```js\nvar div = nom.el('div');\n// = \u003cdiv\u003e\u003c/div\u003e\n```\n\nBoring!\n\n```js\nvar awesome = nom.el('div.awesome');\n// = \u003cdiv class=\"awesome\"\u003e\u003c/div\u003e\n```\n\nAwesome!\n\n### Creating elements with content\n\n`el`'s second parameter is intended for properties. However you can also give it strings (to generate text nodes) and arrays (containing stuff to generate as nodes). You're not creating complex stuff all the time anyway, sometimes you just want simple elements.\n\n```js\nvar div = nom.el('div', 'Text');\n// = \u003cdiv\u003eText\u003c/div\u003e\n```\n\n```js\nvar div = nom.el('div', [\n    nom.el('p', 'Text')\n]);\n// = \u003cdiv\u003e\u003cp\u003eText\u003c/p\u003e\u003c/div\u003e\n```\n\n### Manipulating properties\n\nAt other times you need things to be a little more complex. Besides having child elements you want to have some properties set.\n\n```js\nvar div = nom.el('div', {\n    className: 'awesome',\n    children: 'Text',\n    style: {\n        backgroundColor: 'black',\n        color: 'white'\n    }\n});\n// = \u003cdiv class=\"awesome\" style=\"background-color: black; color: white;\"\u003eText\u003c/div\u003e\n```\n\n### Adding elements to document\n\nSimply use the regular methods you should already be familiar with: `appendChild`, `insertBefore`, `replaceChild`.\n\n```js\ndocument.body.appendChild(\n    nom.el('div', 'I have been added to the DOM.')\n);\n//  \u003c!doctype\u003e\n//  \u003chtml\u003e\n//      \u003chead\u003e...\u003c/head\u003e\n//      \u003cbody\u003e\u003cdiv\u003eI have been added to the DOM.\u003c/div\u003e\u003c/body\u003e\n//  \u003c/html\u003e\n```\n\n### Changing properties\n\nElements created with Nom must be passed via `mount` before the magic happens. Each element can have it's own small routine for updates.\n\n```js\nvar counter = 0;\n\ndocument.body.appendChild(nom.mount(\n    nom.el('div', function() {\n        return {\n            children: 'Render has been called ' + (++counter) + ' times and that is as many times I have been updated.'\n        }\n    })\n));\n```\n\nRemember that in real life code you should never mutate your data like the example above does with the `counter` variable. The code in these property changing functions should only reflect changes in data to the properties. Not create those changes.\n\nNom differs to React here, because React does everything it can to avoid a render call (and thus `setState` requirement for changing data). Nom will call each property function on every render and check if a property has changed. This means Nom code will only force state of a few selected properties of your choice, not take over the entire DOM like React does.\n\n### Object notation\n\nNow we can introduce `els`. It can eat elements created by `el`, and it can take arrays of them, and it can take HTML strings. But most importantly `els` can accept objects with a very compact syntax and create element trees.\n\n```js\nvar tree = nom.els({\n    'div.tree': [\n        '\u003ch1\u003eCan use HTML here to create a header\u003c/h1\u003e',\n        {p: 'Or you can create a paragraph in a much more efficient way!'}\n    ]\n})\n// no need to mount: we have nothing that would re-render itself anyway\ndocument.body.appendChild(tree);\n\n//= \u003cdiv class=\"tree\"\u003e\n//      \u003ch1\u003eCan use HTML here to create a header\u003c/h1\u003e\n//      \u003cp\u003eOr you can create a paragraph in a much more efficient way!\u003c/p\u003e\n//  \u003c/div\u003e\n```\n\n### Dealing with `children` and arrays\n\nUsing data from arrays outside the component scope can be a little bit less obvious to use with Nom. The main thing to avoid is creation of a new Nom element each time the render occurs as this would be a huge contributor to performance and usability issues.\n\nIn general there are two kinds of data within components: component's local state and external state (props in React's language). Dealing with local state (and local arrays) is easier. You have full control over the data and thus you can decide what to do with it. With local state and non-numeric arrays it is possible to simply drop the Nom-initialized element as a property into an array item and then return the element instead of a new Nom element if the array item has the element initialized. This example can be found from the todo demo. On each render `map` is called on array items:\n\n```js\n    // this element reference is needed in add function (could also use `this`)\n    var formTodoInput = nom.el('input', { oninput: edit }, true)\n\n    return nom.els(\n        {h3: state.title},\n        // this is where li elements are created as children of ul\n        {ul: function() { return state.items.map(nomListItem) }},\n        {form: { onsubmit: add, children: [\n            formTodoInput,\n            {button: function() {\n                return { disabled: !state.text, children: 'Add #' + (1 + state.items.length) }\n            }}\n        ] }}\n    )\n```\n\nCreating new elements on each render is a horrible idea, so what does `nomListItem` look like?\n\n```js\n    function nomListItem(item) {\n        // do we already have nomified element for this array item?\n        if (item.nomEl) return item.nomEl\n\n        var itemCheckbox = nom.el('input', function() {\n            return {\n                type: 'checkbox',\n                checked: item.done,\n                onclick: toggle.bind(item)\n            }\n        })\n\n        // store a reference directly to the array item\n        return item.nomEl = nom.el('li', [\n            {label: function() {\n                return {\n                    className: item.done ? 'completed' : '',\n                    children: [ itemCheckbox, ' ' + item.title ]\n                }\n            }}\n        ])\n    }\n```\n\nEssentially this method only works because of **mutability**. `item` is always a reference to an object that may have it's values mutated. These changes are then applied to properties in the label's function.\n\nBut what if data comes from the outside? It is not a good idea to mutate data which is not our own. The above does not answer that question.\n\n# Requirements\n\nNom doesn't really need anything if you're using a modern browser. However to improve browser support it is a good idea to consider the following:\n\n1. [es5-shim](https://github.com/es-shims/es5-shim) or [core-js](https://github.com/zloirock/core-js)\n2. [requestAnimationFrame polyfill](https://gist.github.com/paulirish/1579671) (**note!** the one provided here on Nom's repo requires es5-shim)\n\nNom's code is written so that it should run flawlessly on itself even on engines as old as IE5 as long as shim for ECMAScript 5 and polyfill for requestAnimationFrame are provided. Nom doesn't care about specific special issues that these old browser engines have. For example, you can't change `\u003cinput type=\"password\" /\u003e` to `\u003cinput type=\"text\" /\u003e` on the fly in IE8 and earlier. That is out of scope for Nom.\n\nWhat **is** within Nom's scope are standard methods. `oninput` is patched by Nom to work cross-browser from IE8ish onwards. It isn't patched entirely (event listeners are not supported), but just enough so that you don't need to hack around with multitude of other events like `onfocus`, `onkeypress` and so on. At the moment of writing (2015-05-01) this is the only notable workaround and IE-specificity within Nom.\n\n## With no external help applied Nom works on...\n\n- Firefox 23+\n- Chrome 24+\n- Internet Explorer 10+\n- Safari 6.1+\n- iOS Safari 7+\n- Android 4.4+\n- Opera 15+\n- Opera Mobile 24+\n- IE Mobile 10+\n\n[Can I Use... requestAnimationFrame](http://caniuse.com/#search=requestanimationframe) shows quite a good approximation of the minimum browser support without any aid.\n\n# TODO\n\nNom is still young and in need of more polish. These things need to be done:\n\n1. Tests and running them on multiple browsers.\n2. A Real Project using Nom for dealing with the DOM.\n3. Documentation showcasing useful patterns and telling what to avoid.\n\nThe rest will be just Nom's natural evolution and evaluation of what features it needs to provide to ease development pain and which things it can ignore and let remain a developer's issue (cross-browser ones).\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmerri%2Fnom","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmerri%2Fnom","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmerri%2Fnom/lists"}