{"id":19853887,"url":"https://github.com/magnetikonline/picoh","last_synced_at":"2026-05-09T23:09:02.814Z","repository":{"id":9068177,"uuid":"10838669","full_name":"magnetikonline/picoh","owner":"magnetikonline","description":"Yet another JavaScript frontend micro framework - minified and gzipped to around 2.3KB.","archived":false,"fork":false,"pushed_at":"2022-01-31T00:46:29.000Z","size":64,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-01-11T13:45:42.561Z","etag":null,"topics":["css-animations","css-transitions","dom","frontend-framework","xmlhttp"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"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/magnetikonline.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":"2013-06-21T08:32:35.000Z","updated_at":"2022-01-31T00:46:32.000Z","dependencies_parsed_at":"2022-08-28T02:11:55.968Z","dependency_job_id":null,"html_url":"https://github.com/magnetikonline/picoh","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/magnetikonline%2Fpicoh","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/magnetikonline%2Fpicoh/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/magnetikonline%2Fpicoh/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/magnetikonline%2Fpicoh/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/magnetikonline","download_url":"https://codeload.github.com/magnetikonline/picoh/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":241243727,"owners_count":19933182,"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":["css-animations","css-transitions","dom","frontend-framework","xmlhttp"],"created_at":"2024-11-12T14:07:58.463Z","updated_at":"2026-05-09T23:09:02.775Z","avatar_url":"https://github.com/magnetikonline.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Picoh\nPicoh is my take on the JavaScript frontend micro framework, providing event handling, DOM querying/manipulation, XMLHTTP requests and a handful of utility methods.\n\nThe focus is on a lean code footprint - minified and gzipped everything weighs in around **2.3KB**.\n\nAll methods are namespaced under `window.$` / `window.$$` with no modification of *any* object prototypes.\n\n## What's supported?\n- Picoh takes advantage of core methods available in more modern browsers (such as `document.querySelectorAll()`) avoiding heavy polyfills to keep the code footprint small.\n- Designed for and tested against the usual suspects of Google Chrome, Firefox and OSX Safari. On the Microsoft front **IE9 and above** is supported.\n\t- The final revision supporting IE8 is [tagged here](https://github.com/magnetikonline/picoh/tree/ie8-final).\n- Expects:\n\t- A `!DOCTYPE` that puts your pages into *standards mode*, with the [HTML5 doctype](https://w3c.github.io/html/syntax.html#the-doctype) a good selection. More a requirement for full IE compatibility (historically an IE8 gripe), where some core methods used by Picoh [won't make themselves available](https://caniuse.com/#feat=json) in 'quirks mode'. You *can* work around such edge cases, but the result is increased code footprint - exactly what I'm trying to avoid here.\n\t- Picoh loaded ideally from page `\u003chead\u003e` and executed asynchronously (e.g. `\u003cscript async src=\"/uri/to/picoh.js\"\u003e\u003c/script\u003e`).\n\t- Removal of both `margin` and `padding` from the `\u003cbody\u003e` element to help cross browser consistency with some DOM methods, such as calculating [viewport](#domgetviewportsize) and [document](#domgetdocumentsize) sizes.\n\n## Methods\n- [General](#general)\n\t- [$(id)](#id)\n\t- [$.debounce(handler,delay)](#debouncehandlerdelay)\n\t- [$.each(collection,handler)](#eachcollectionhandler)\n\t- [$.nextTick(handler)](#nexttickhandler)\n\t- [$.reqAnimFrame(handler)](#reqanimframehandler)\n- [Events](#events)\n\t- [$.Event.add(object,type,handler)](#eventaddobjecttypehandler)\n\t- [$.Event.remove(object,type,handler)](#eventremoveobjecttypehandler)\n\t- [$.Event.getTarget(event)](#eventgettargetevent)\n\t- [$.Event.isMouseEnterLeave(event,element)](#eventismouseenterleaveeventelement)\n\t- [$.Event.getMousePosition(event)](#eventgetmousepositionevent)\n- [DOM](#dom)\n\t- [$$(query) / $$(element,query)](#query--elementquery)\n\t- [$.DOM.ready(handler)](#domreadyhandler)\n\t- [$.DOM.create(name[,attributeList][,childElementList])](#domcreatenameattributelistchildelementlist)\n\t- [$.DOM.insertBefore(element,referenceElement)](#dominsertbeforeelementreferenceelement)\n\t- [$.DOM.insertAfter(element,referenceElement)](#dominsertafterelementreferenceelement)\n\t- [$.DOM.replace(element,oldElement)](#domreplaceelementoldelement)\n\t- [$.DOM.remove(element)](#domremoveelement)\n\t- [$.DOM.removeChildAll(element)](#domremovechildallelement)\n\t- [$.DOM.hasClass(element,name)](#domhasclasselementname)\n\t- [$.DOM.addClass(element,name)](#domaddclasselementname)\n\t- [$.DOM.removeClass(element,name)](#domremoveclasselementname)\n\t- [$.DOM.setStyle(element,styleList)](#domsetstyleelementstylelist)\n\t- [$.DOM.getData(element,key)](#domgetdataelementkey)\n\t- [$.DOM.getOffset(element[,toParent])](#domgetoffsetelementtoparent)\n\t- [$.DOM.getPageScroll()](#domgetpagescroll)\n\t- [$.DOM.getViewportSize()](#domgetviewportsize)\n\t- [$.DOM.getDocumentSize()](#domgetdocumentsize)\n- [Animation/transition end DOM events](#animationtransition-end-dom-events)\n\t- [$.DOM.Anim.onAnimationEnd(element,handler[,data])](#domanimonanimationendelementhandlerdata)\n\t- [$.DOM.Anim.cancelAnimationEnd(element)](#domanimcancelanimationendelement)\n\t- [$.DOM.Anim.onTransitionEnd(element,handler[,data])](#domanimontransitionendelementhandlerdata)\n\t- [$.DOM.Anim.cancelTransitionEnd(element)](#domanimcanceltransitionendelement)\n- [XMLHTTP](#xmlhttp)\n\t- [$.request(url[,method][,handler][,parameterCollection])](#requesturlmethodhandlerparametercollection)\n- [Miscellaneous](#miscellaneous)\n\t- [*Has JavaScript?* CSS class hook](#has-javascript-css-class-hook)\n\t- [Attachment of Picoh to alternative object](#attachment-of-picoh-to-alternative-object)\n\n### General\n\n#### $(id)\nReturns a single DOM element from the `id` given. Just a wrapper around `document.getElementByID()` we all know and love.\n\n#### $.debounce(handler,delay)\n- Wraps the given `handler` in a debounce routine that will be called only after `delay` milliseconds have elapsed since last call to the routine was made.\n- A `clear()` method allows for the reset of the current debounce timeout in progress.\n\nExample:\n\n```js\nfunction callMe() {\n\n\tconsole.log('Called');\n}\n\nvar debounceMe = $.debounce(callMe,500);\n\n// log message will only display once, though debounceMe() is called three times\ndebounceMe();\ndebounceMe();\ndebounceMe();\n\n// to clear a current debounce timeout\n//debounceMe.clear();\n```\n\n#### $.each(collection,handler)\n- Iterate over the given `collection`, calling `handler` for each item.\n- Collection can be of type `array`, `HtmlCollection`, `NodeList` or `object`.\n- `handler` passed arguments of `value` and `index` for types `array`, `HtmlCollection` and `NodeList`.\n- `handler` passed arguments of `value`, `key` name and `iteration` count for type `object`.\n- Returning `false` from `handler` will halt the iteration immediately.\n\nExample:\n\n```js\nfunction handlerArray(value,index) {\n\n\tconsole.log([value,index].join(' - '));\n}\n\n$.each(\n\t[1,2,3,4],\n\thandlerArray\n);\n\nfunction handlerObject(value,key,iteration) {\n\n\tconsole.log([value,key,iteration].join(' - '));\n\n\tif (key == 'key3') {\n\t\t// exit right away\n\t\treturn false;\n\t}\n}\n\n$.each(\n\t{ key1: 'value1',key2: 'value2',key3: 'value3',key4: 'value4' },\n\thandlerObject\n);\n```\n\n**Note:** For evaluating `HTMLCollection`/`NodeList` types, `$.each()` uses [Duck typing](https://en.wikipedia.org/wiki/Duck_typing), looking for `item` and `length` properties.\n\n#### $.nextTick(handler)\n- Emulation of Node.js [`process.nextTick()`](https://nodejs.org/api/process.html#process_process_nexttick_callback_args) method.\n- Produces a faster and [more efficient](https://dbaron.org/log/20100309-faster-timeouts) callback on the next event loop vs. `window.setTimeout(function() {},0)`.\n- Implemented using `window.postMessage()` under the hood.\n\n#### $.reqAnimFrame(handler)\n- Wrapper for `window.requestAnimationFrame`, a more efficient method of processing animation frames versus traditional `window.setTimeout()` use.\n- Handles cross browser API prefixing between browser vendors.\n- A fallback `window.setTimeout()` polyfill is provided for unsupported browsers which will be called approximately once every 16ms to give a *close to* 60fps fire rate.\n\n### Events\n\n#### $.Event.add(object,type,handler)\n- Attach an event `handler` to the given `object` of the given `type`.\n- Event `type` can be given as a space separated list for attaching multiple events to a single `object`.\n\nExample:\n\n```js\nfunction clickTouchHandler(event) {\n\n\tconsole.log('Clicked or touched!');\n\tconsole.log(this);\n\tconsole.log(event);\n}\n\n$.Event.add($('domelement'),'click touchstart',clickTouchHandler);\n```\n\n#### $.Event.remove(object,type,handler)\n- Remove an event `handler` from the given `object` of the given `type`.\n- Event `type` can be given as a space separated list for removing multiple events from a single `object`.\n\n#### $.Event.getTarget(event)\n- Returns the DOM element that the given `event` was dispatched on.\n- Handles the edge case with older versions of Safari where a [text node would be incorrectly returned](https://bugs.jquery.com/ticket/5539).\n\n#### $.Event.isMouseEnterLeave(event,element)\nEmulates behavior of the mighty handy and IE only (**note:** Chrome 30+ and Firefox 10+ also natively support) event types of [mouseenter](https://developer.mozilla.org/en-US/docs/Web/Events/mouseenter) and [mouseleave](https://developer.mozilla.org/en-US/docs/Web/Events/mouseleave).\n\nExample:\n\n```html\n\u003cdiv id=\"watchme\"\u003e\n\t\u003cspan\u003eChild element\u003c/span\u003e\n\t\u003cspan\u003eAnother child element\u003c/span\u003e\n\u003c/div\u003e\n```\n\n```js\nfunction mouseEnterHandler(event) {\n\n\tif ($.Event.isMouseEnterLeave(event,this)) {\n\t\tconsole.log('mouseenter!');\n\t}\n}\n\nfunction mouseLeaveHandler(event) {\n\n\tif ($.Event.isMouseEnterLeave(event,this)) {\n\t\tconsole.log('mouseleave!');\n\t}\n}\n\nvar watchMeEl = $('watchme');\n$.Event.add(watchMeEl,'mouseover',mouseEnterHandler);\n$.Event.add(watchMeEl,'mouseout',mouseLeaveHandler);\n```\n\nWith above example messages will only log messages when mouse pointer *enters* or *leaves* `\u003cdiv id=\"watchme\"\u003e`, ignoring all mouseover/mouseout child events fired from `\u003cspan\u003e` elements.\n\n#### $.Event.getMousePosition(event)\n- Returns the current mouse x/y pixel coordinates from the given `event`.\n- Data will be returned as an object with the structure of `{ x: 123,y: 456 }`.\n- **Note:** At time of writing IE10 (and possibly other browser vendors going forward) can/will return mouse coordinates with a sub-pixel resolution, `getMousePosition()` will round down to whole pixel units.\n\n### DOM\n\n#### $$(query) / $$(element,query)\n- A wrapper for [`querySelectorAll()`](https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelectorAll), returning DOM elements for the given CSS `query`.\n- In the first form the query will be based from `document` (entire page), otherwise in the second form from the given `element`.\n- In the instance `query` is matching elements containing one or more classes _only_ (e.g. `.apple.orange.banana`) the function will use [`getElementsByClassName()`](https://developer.mozilla.org/en-US/docs/Web/API/Document/getElementsByClassName) in place of `querySelectorAll()` for query speed advantage.\n- Returned DOM elements will be provided as an array, rather than a `NodeList`.\n- **Note:** `querySelectorAll()` will only support queries based upon the browsers CSS implementation and it's capabilities.\n\n#### $.DOM.ready(handler)\n- Will call the given `handler` upon firing of the `document.DOMContentLoaded` event.\n- Can be called with multiple `handler` functions, each of which be called in turn at the point of DOM load completion.\n- If called *after* DOM has already loaded, the given `handler` will execute immediately via [$.nextTick(handler)](#nexttickhandler).\n\n#### $.DOM.create(name[,attributeList][,childElementList])\n- Creates a new DOM element with the given node `name`.\n- Optional attributes given as a key/value object `attributeList`.\n\t- Keys are to be given as DOM element properties (e.g. `class=\"myclass\"` as `{ className: 'myclass' }`.\n- Optional child DOM elements automatically appended given as an array `childElementList`.\n\t- Child elements of type `string` will be appended as a new `TextNode`.\n\nExample:\n\n```js\nvar myCreatedDOMEl = $.DOM.create(\n\t'div',{ className: 'myclass' },\n\t[\n\t\t$.DOM.create('span',false,['My span text']),\n\t\t$.DOM.create('a',{ href: '/link/to/item' },['Click me']),\n\t\t'Another line of text'\n\t]\n);\n\n// append the following tag structure to end of the document\n/*\n\u003cdiv class=\"myclass\"\u003e\n\t\u003cspan\u003eMy span text\u003c/span\u003e\n\t\u003ca href=\"/link/to/item\"\u003eClick me\u003c/a\u003e\n\tAnother line of text\n\u003c/div\u003e\n*/\n\ndocument.documentElement.appendChild(myCreatedDOMEl);\n```\n\n#### $.DOM.insertBefore(element,referenceElement)\nInsert the given `element` before `referenceElement` within the current document.\n\n#### $.DOM.insertAfter(element,referenceElement)\nInsert the given `element` after `referenceElement` within the current document.\n\n#### $.DOM.replace(element,oldElement)\nReplace the given `oldElement` within the current document with `element`. Returns `oldElement`.\n\n#### $.DOM.remove(element)\nRemove the given `element` from the DOM, returning `element`.\n\n#### $.DOM.removeChildAll(element)\nRemove all child DOM elements from the given `element`, returning an array of removed elements.\n\n#### $.DOM.hasClass(element,name)\nReturns `true` if `element` has the given CSS class `name` assigned, otherwise return `false`.\n\n#### $.DOM.addClass(element,name)\n- Add one or more CSS classes of the given `name` to `element` - providing multiple CSS class names space separated.\n- CSS classes already present on `element` will be silently ignored.\n\n#### $.DOM.removeClass(element,name)\n- Remove one or more CSS classes of the given `name` from `element`.\n- Provide multiple CSS class names for removal space separated.\n\n#### $.DOM.setStyle(element,styleList)\n- Set the given inline CSS `styleList` (as a key/value object) to `element`. Essentially an easier way to set multiple inline element style attributes at once.\n- Internally uses a simplistic `element.style.[styleKey] = value` assignment, therefore `styleList` key(s) must be given using camel cased style names (e.g. `backgroundColor`).\n\nExample:\n\n```js\nvar myCreatedDOMEl = $.DOM.create('div',false,['Content']);\n$.DOM.setStyle(\n\tmyCreatedDOMEl,\n\t{\n\t\tbackgroundColor: '#f00',\n\t\tleft: '10px',\n\t\tposition: 'absolute',\n\t\ttop: '40px'\n\t}\n);\n\n// myCreatedDOMEl contains\n/*\n\u003cdiv style=\"background-color:#f00;left:10px;position:absolute;top:40px\"\u003e\n\tContent\n\u003c/div\u003e\n*/\n```\n\n#### $.DOM.getData(element,key)\n- Returns the value of the [HTML5 data attribute](https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Using_data_attributes) `key` associated to `element`.\n- If the given `key` does not exist returns `null`.\n\n#### $.DOM.getOffset(element[,toParent])\n- Returns the left/top pixel offset of the given `element` to either the top left corner of the document, or if `toParent` is `true` - to the element's parent.\n- Data will be returned as an object with the structure of `{ left: 123,top: 456 }`.\n\n#### $.DOM.getPageScroll()\n- Returns the x/y pixel scroll offset from the top left corner of the document.\n- Data will be returned as an object with the structure of `{ x: 123,y: 456 }`.\n\n#### $.DOM.getViewportSize()\n- Returns the pixel width and height of the browser viewport.\n- Data will be returned as an object with the structure of `{ width: 123,height: 456 }`.\n\n#### $.DOM.getDocumentSize()\n- Returns the pixel width and height of the document.\n- Data will be returned as an object with the structure of `{ width: 123,height: 456 }`.\n- Uses the techniques suggested by [Ryan Van Etten](https://ryanve.com/lab/dimensions/#document).\n\n### Animation/transition end DOM events\nFor the background behind these methods and their use, refer to the [cssanimevent](https://github.com/magnetikonline/cssanimevent) library. The following methods have been integrated here.\n\n#### $.DOM.Anim.onAnimationEnd(element,handler[,data])\n- Calls the given `handler` upon completion of a [CSS3 animation](https://developer.mozilla.org/en/docs/Web/CSS/animation) applied to `element`. Lifetime of the handler is *one* animation end event.\n- For browsers that do not support CSS3 animations, `handler` will be called instantaneously.\n- Handler will be passed arguments of `element` and optional `data`.\n\n#### $.DOM.Anim.cancelAnimationEnd(element)\nCancel a pending handler assigned to `element` by a previous call to `$.DOM.Anim.onAnimationEnd()`.\n\n#### $.DOM.Anim.onTransitionEnd(element,handler[,data])\nIdentical in functionality to `$.DOM.Anim.onAnimationEnd()`, but for [CSS3 transitions](https://developer.mozilla.org/en/docs/Web/CSS/transition).\n\n#### $.DOM.Anim.cancelTransitionEnd(element)\nIdentical in functionality to `$.DOM.Anim.cancelAnimationEnd()`, but for CSS3 transitions.\n\n### XMLHTTP\n\n#### $.request(url[,method][,handler][,parameterCollection])\n- Execute a `XMLHttpRequest()` call to the given `url`, returning `true` if the call was successful made by the browser (e.g. supports `XMLHttpRequest()`).\n- The `method` can be one of `GET` or `POST`, with `false`/`undefined` defaulting to `GET`.\n- Optional `handler` will be executed at completion of the URL call (success or fail). Handler will be passed a single parameter of the return status/response as an object with the following keys:\n\t- `ok:` Set `true` if the call returned successfully, otherwise `false`.\n\t- `status:` Numeric HTTP status code returned.\n\t- `text:` The response body as a string upon success, otherwise empty string.\n\t- `JSON:` If response body is JSON data and could be successfully parsed, will contain a JavaScript object of this data, otherwise an empty object.\n- Optional `parameterCollection` given as key/value pairs with be passed either:\n\t- On the query string with HTTP `GET`.\n\t- Form data of content type `application/x-www-form-urlencoded` with HTTP `POST`.\n\nExample:\n\n```js\nfunction myHandler(data) {\n\n\tif (data.ok) {\n\t\tconsole.log('HTTP status: ' + data.status);\n\t\tconsole.log('Response text: ' + data.text);\n\t\tconsole.dir(data.JSON);\n\n\t} else {\n\t\t// handle error\n\t}\n}\n\n// make a POST request to /xmlhttp/endpoint with parameters \"key1=value,key2=value\"\n$.request(\n\t'/xmlhttp/endpoint',\n\t'POST',\n\tmyHandler,\n\t{\n\t\tkey1: 'value',\n\t\tkey2: 'value'\n\t}\n);\n```\n\n### Miscellaneous\n\n#### *Has JavaScript?* CSS class hook\n- Placing a class attribute of `\u003chtml class=\"nojs\"\u003e` on a document's `\u003chtml\u003e` element will be automatically replaced with `\u003chtml class=\"js\"\u003e` upon load of Picoh.\n- Used as a CSS styling hook for no/has JavaScript scenarios.\n\n#### Attachment of Picoh to alternative object\n- By default Picoh is attached to the global `window` object, providing access to it's methods via `window.$` and `window.$$()` respectively, or simply `$` and `$$()`.\n- Alternatively, Picoh can be attached to an isolated object to avoid namespace clashes, by modification of the library's [IIFE](https://en.wikipedia.org/wiki/Immediately-invoked_function_expression) arguments.\n\nExample:\n\n```js\nvar attachHere = {};\n\n// start of Picoh\n(function(win,doc,picohAttach,undefined) {\n\n\t// SNIP\n})(window,document,attachHere);\n```\n\nPicoh will be accessible at `attachHere.$`/`attachHere.$$()`.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmagnetikonline%2Fpicoh","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmagnetikonline%2Fpicoh","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmagnetikonline%2Fpicoh/lists"}