{"id":24448613,"url":"https://github.com/nbubna/trigger","last_synced_at":"2025-06-15T15:35:44.292Z","repository":{"id":8302292,"uuid":"9845505","full_name":"nbubna/trigger","owner":"nbubna","description":"Superior events for single page web apps.","archived":false,"fork":false,"pushed_at":"2013-08-13T18:16:44.000Z","size":594,"stargazers_count":27,"open_issues_count":0,"forks_count":1,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-06-13T01:05:05.888Z","etag":null,"topics":[],"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/nbubna.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE-MIT","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2013-05-03T22:03:01.000Z","updated_at":"2023-09-08T16:39:13.000Z","dependencies_parsed_at":"2022-08-17T15:45:49.815Z","dependency_job_id":null,"html_url":"https://github.com/nbubna/trigger","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/nbubna/trigger","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nbubna%2Ftrigger","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nbubna%2Ftrigger/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nbubna%2Ftrigger/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nbubna%2Ftrigger/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nbubna","download_url":"https://codeload.github.com/nbubna/trigger/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nbubna%2Ftrigger/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":259998179,"owners_count":22943767,"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":"2025-01-21T00:32:18.829Z","updated_at":"2025-06-15T15:35:44.268Z","avatar_url":"https://github.com/nbubna.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"##### Because custom events should be easy...\n\nDownload: [trigger.min.js][prod]  or  [trigger.js][dev]  \nBower: `bower install trigger`  \n[NPM][npm]: `npm install trigger`  \n[NuGet][]: `Install-Package trigger`  \n\n[NuGet]: http://nuget.org/packages/trigger/\n[prod]: https://raw.github.com/nbubna/trigger/master/dist/trigger.min.js\n[dev]: https://raw.github.com/nbubna/trigger/master/dist/trigger.js\n[npm]: https://npmjs.org/package/trigger\n\n#### Meaningless Events Are Lame\nEvents like 'click' and 'keyup' are meaningless to the models and logic of most applications,\nbut i bet you still register listeners for them in your application's code. This is lame.\n\nYour application code only needs to know what a particular event means\n(e.g. 'save', 'delete', 'next', etc). Your app's ideal javascript would only\nregister listeners for events that are meaningful to your specific application (i.e. custom events).\n\n### Declarative Application Events Are Better\nAdd trigger.js to your page, then simply declare what 'click' means in your markup:\n```html\n\u003cbutton click=\"save\"\u003eSave\u003c/button\u003e\n```\nWhen the user \"clicks\" it, a 'save' event is automatically created and dispatched on the element.\nYour application never needs to listen for a click event again. This makes your JavaScript more\nreadable and your HTML more self-explanatory.\n\nMost people can get by with just declaring the meaning of clicks,\nbut you can easily add other native events as additional triggers:  \n```html\n\u003chtml trigger-add=\"dblclick drop\"\u003e\n...\n\u003cdiv class=\"folder\" dblclick=\"open\" drop=\"move\"\u003e...\u003c/div\u003e\n```\n\n\n#### Dependent Events Are Tricky\nSometimes a single \"click\" serves as a trigger for a sequence of application actions.\nThe simpler apps out there just conflate the actions into one `$(form).click(saveIfValid)`.\nMore advanced developers might register multiple listeners for the same event and\nuse `event.stopImmediatePropogation();` when you need to break the sequence.\nBut both approaches (and most varieties of them) are fundamentally hacks to workaround\nyou wanting a single browser event to start a sequence of application events.\nThere is a better way...\n\n### Declaring Event Sequences Is Easy\n```html\n\u003cinput type=\"submit\" click=\"validate save\"\u003e\n```\nThis will trigger the \"validate\" and \"save\" events in sequence.\nYour list of events can be as long as you like. Any event handler can just call\n`event.stopSequence()` to stop the rest of the specific, declared sequence.\nThen, if you like, you can call `event.resumeSequence()` to restart it where you left off.\nAnd of course, check on the state of things with `event.isSequenceStopped()`.\n\n\n#### Asynchronous Handlers Make Messes\nOnce you are used to chaining events into nice declarative sequences,\nyou will likely come upon a situation where one of the handlers needs to do something\nasynchronous (e.g. validate something on the server) before the subsequent events are\ntriggered. To keep things event-y, you do a manual `trigger('save');` call at\nthe end of the success callback for your async business.  But this means your nice\ndeclarative `\u003cbutton click=\"validate save\"\u003eSave\u003c/button\u003e` element becomes a\nconfusing `\u003cbutton click=\"validate\"\u003eSave\u003c/button\u003e`.\n\n### Promise-Friendly Event Sequences Are Clean And Tidy\nIt's easy, get yourself a [promise][] in that `validate` event handler and set it\non the event (e.g. `event.stopSequence(promise);`). This stops the event sequence\nand automatically resumes it again once the promise is fulfilled. Now you\ncan have your straightforward `click=\"validate save\"` button back!\n\n[promise]: http://wiki.commonjs.org/wiki/Promises/A\n\n\n#### Simplistic Events Can Be Awkward\nOnce you've earned your \"Application Events\" achievement, you may realize you are only declaring\nevents as disconnected verbs or nouns, or maybe awkward verbNouns. Your listeners have to\nglean information from the context or target element to decipher the full meaning of the event.\nThis can easily lead you away from descriptive code and into \"use comments to explain\" territory.\nSometimes that simplicity is good, but sometimes it is a real problem.\n\n### Grammatically Rich Events Can Be Graceful\ntrigger.js provides a declarative syntax for grammatically rich events.\nThis helps you level-up the self-documentation of your javascript and HTML\nand simplify your event listeners.\n\n##### click=\"category:type\" -\u003e event.category\nWhen you need to distinguish your player's \"move\" event from that of a different feature,\nprefix your event with a category (subject/noun): `click=\"player:move\"`.\nAny app-wide 'move' listener can read it from the `event.category` property.\n\n##### click=\"type['constant','other']\" -\u003e event.constants\nTo include contextual constants (object/noun) for your event, do: `click=\"view['start']\"`\nThe constant gets the JSON.parse() treatment (after some quote massaging) and\nis set at `event.constants` (always in an array, thus the brackets);\n\n##### click=\"type#tag\" -\u003e event.tags \u0026 event[tag]\nFinally, you can add simple tags (adjectives/adverbs) to your events, each prefixed by '#':\n`click=\"move#up#left\"` and listen for these at `event.tags` and each `event[tag]`\n(the individual tags are always given a value of `true`).\n\nNOTE: If you have a reason to use combinations of all three (probably rare),\nthen you ***must*** put them in this order: `category:type['constant']#tags`\n(e.g. `click=\"player:move[{'speed':2}]#west\"`).\nThink of it as subject, verb, object, adjectives and you probably won't forget how it goes.\n\n\n\n#### But HTML Validation?!\nYou probably understand why [HTML validation is considered harmful][invalid],\nbut your pointy-haired boss still labors under the naive impression that it is a best practice.\n\n### Ok, fine, you can have your 'data-' prefix\n```html\n\u003chtml data-trigger=\"true\" data-trigger-add=\"dblclick\"\u003e\n...\n\u003cbutton data-click=\"validateHTML\"\u003e\"be lame\"\u003c/button\u003e\n```\n\n[invalid]: http://wheelcode.blogspot.com/2012/07/html-validation-is-bad.html\n\n#### But Old IE?!?\nYou aren't ready to abandon the poor saps still using ancient versions of IE.\nSure, Google stopped supporting them, but you aren't Google.\n\n#### Can do.\nJust use jQuery (of course) and this [tiny extension][old]:\n```html\n\u003c!--[if lt IE 9]\u003e\n  \u003cscript src=\"../src/trigger.old.js\"\u003e\u003c/script\u003e\n\u003c![endif]--\u003e\n```\n\n[old]: https://raw.github.com/nbubna/trigger/master/src/trigger.old.js\n\n\n#### Another Small Extension\nIf you see yourself manually using trigger instead of always letting browser events\nserve as triggers and also happen to be fond of jQuery, [jquery.trigger.js][jquery]\nallows you to do `$('#foo').trigger('foo:squish#gooey');` instead of\n`trigger($('#foo')[0], 'foo:squish#gooey');`.\n\n[jquery]: https://raw.github.com/nbubna/trigger/master/src/jquery.trigger.js\n\n\n#### Short jQuery Version - For Those Who Don't Need All The Features\n```javascript\n$(document).on('click', function(e) {\n  var $el = $(e.target).closest('[click]'),\n      events = $el.attr('click') || '';\n  events.split(' ').forEach(function(event) {\n    $el.trigger(event);\n    if (!$(e.target).is('[type=radio],[type=checkbox]')) e.preventDefault();\n  });\n});\n```\n\n\n#### Mini-Example, Just For Fun\n```html\n\u003cdiv id=\"#dungeonPlunge\"\u003e\n  \u003cinput type=\"dice\" name=\"roll\"\u003e\n  \u003cbutton click=\"move#up nextPlayer\"\u003eClimb\u003c/button\u003e  \n  \u003cbutton click=\"move#down nextPlayer\"\u003eSlide\u003c/button\u003e\n\u003c/div\u003e\n```\n```javascript\nvar game = document.querySelector('#dungeonPlunge');\ngame.addEventListener('nextPlayer', function() {\n  player = player.next;\n});\ngame.addEventListener('move', function(e) {\n  var distance = game.querySelector('[name=roll]').value;\n  if (e.up) player.climb(distance);\n  if (e.down) player.slide(distance);\n  if (player.hasWon()) e.stopSequence();//blocks nextPlayer event\n});\n```\n  \n\n### Advanced Details\n##### 'click'-ish secrets\n * Clicks are ignored if their target was a user-editable field (e.g. textarea) that did not\nhave a click attribute itself, but was a child of an element that did have one.\n * Enter keyups (keyCode:13) are treated as clicks if their target lacks a \"native response\"\nto such events (e.g. in a textarea, it adds a new line, or on a link, it causes a click).\nThe exception being if such an element has a `keyup` or `key-enter` attribute declared on it.\n * When a click is used by trigger.js, it will automatically prevent the original event's\ndefault behavior, except in the case of radio buttons and checkboxes. The assumption is\nthat the default behavior is replaced by the declared event sequence.\n\n##### trigger._.special\nThis extension hook provides you the opportunity to change event types, with some particular\naid for tweaking events that have a 'which' or 'keyCode' important to you. Here's an example:\n\n```html\n\u003cdiv tabIndex=\"0\" key-del=\"delete\" click=\"edit\"\u003e\n  \u003cspan\u003eNathan Bubna\u003c/span\u003e\n  \u003cinput type=\"text\" key-esc=\"cancel\" key-enter=\"save\"\u003e\n\u003c/div\u003e\n```\n```javascript\n$.extend(trigger._.special, {\n  keyup27: function(e){ return 'key-esc'; },\n  keyup46: function(e){ return 'key-del'; }\n});\n```\nNote: trigger.js already listens for `keyup` and `click` events. For other special events,\nlike keydown or dblclick, remember to do `\u003chtml trigger-add=\"keydown\"\u003e` or the like.\n\nTODO: add more advanced details...\n\n\n#### Release History\n* 2010-04-02 v0.1 (internal)\n* 2012-09-13 v0.3 (internal)\n* 2013-05-03 v0.9.0 (public) - First GitHub release\n* 2013-05-16 v1.0.0 (public) - tests and feature completeness\n* 2013-05-21 v1.1.0 (public) - declarative configuration\n* 2013-05-22 v1.1.1 (public) - fix build problem\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnbubna%2Ftrigger","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnbubna%2Ftrigger","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnbubna%2Ftrigger/lists"}