{"id":32241305,"url":"https://github.com/jonathanweiss/component-manager","last_synced_at":"2026-02-19T23:02:17.351Z","repository":{"id":57391873,"uuid":"65084959","full_name":"jonathanweiss/component-manager","owner":"jonathanweiss","description":"Micro utility (583 bytes compressed) that manages initialising and teardown of components (e.g. jQuery plugins) as soon as specific DOM nodes are added into the document, respectively removed from it.","archived":false,"fork":false,"pushed_at":"2020-05-28T22:44:11.000Z","size":290,"stargazers_count":7,"open_issues_count":22,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2026-01-24T07:43:07.092Z","etag":null,"topics":["components","manager","microlib","mutationobserver","utility-library","vanilla-javascript","vanilla-js"],"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/jonathanweiss.png","metadata":{"files":{"readme":"README.md","changelog":"changelog.md","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":"2016-08-06T13:59:17.000Z","updated_at":"2020-05-12T04:14:26.000Z","dependencies_parsed_at":"2022-08-30T14:51:28.254Z","dependency_job_id":null,"html_url":"https://github.com/jonathanweiss/component-manager","commit_stats":null,"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"purl":"pkg:github/jonathanweiss/component-manager","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jonathanweiss%2Fcomponent-manager","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jonathanweiss%2Fcomponent-manager/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jonathanweiss%2Fcomponent-manager/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jonathanweiss%2Fcomponent-manager/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jonathanweiss","download_url":"https://codeload.github.com/jonathanweiss/component-manager/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jonathanweiss%2Fcomponent-manager/sbom","scorecard":{"id":530556,"data":{"date":"2025-08-11","repo":{"name":"github.com/jonathanweiss/component-manager","commit":"84694aeba02ebc76e46b816dbf0a5498e2671309"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3.5,"checks":[{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Code-Review","score":0,"reason":"Found 0/13 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":-1,"reason":"internal error: error during branchesHandler.setup: internal error: githubv4.Query: Resource not accessible by integration","details":null,"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 26 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-20T05:36:43.771Z","repository_id":57391873,"created_at":"2025-08-20T05:36:43.771Z","updated_at":"2025-08-20T05:36:43.771Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29636040,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-19T22:32:43.237Z","status":"ssl_error","status_checked_at":"2026-02-19T22:32:38.330Z","response_time":117,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":["components","manager","microlib","mutationobserver","utility-library","vanilla-javascript","vanilla-js"],"created_at":"2025-10-22T15:29:22.560Z","updated_at":"2026-02-19T23:02:17.339Z","avatar_url":"https://github.com/jonathanweiss.png","language":"JavaScript","readme":"# ComponentManager\n\n[![Size](https://img.shields.io/badge/min+gz-583%20b-blue.svg)](https://unpkg.com/verwalter/dist/manager.min.js)\n[![Build](https://api.travis-ci.org/jonathanweiss/component-manager.svg?branch=master)](https://travis-ci.org/jonathanweiss/component-manager/)\n[![Build Status](https://app.saucelabs.com/buildstatus/jonathanfweiss)](https://app.saucelabs.com/builds/e0be3fe52a43466f8de9235210e900e8)\n[![Coverage Status](https://coveralls.io/repos/github/jonathanweiss/component-manager/badge.svg?branch=master)](https://coveralls.io/github/jonathanweiss/component-manager?branch=master)\n[![semantic-release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg)](https://github.com/semantic-release/semantic-release)\n[![JavaScript Style Guide](https://img.shields.io/badge/code%20style-standard-brightgreen.svg)](http://standardjs.com/)\n[![Version](https://img.shields.io/npm/v/verwalter.svg?maxAge=2592000)](https://www.npmjs.com/package/verwalter)\n[![License](https://img.shields.io/github/license/mashape/apistatus.svg)](https://opensource.org/licenses/mit-license.php)\n\n## What is this?\nUtility that takes care of creating and destroying instances (e.g. of jQuery plugins) as soon as specific DOM nodes are added to the document, respectively removed from the document.\nUniversal  minimal (1.2 KB, 583 bytes compressed) helper with zero dependencies.\n\n## (Longer) Introduction\nMany websites or web apps examine the DOM and create instances (e.g. of jQuery plugins) for specific DOM nodes during the initial load. Often a class name indicates what kind of instance should be created for a specific DOM Node.\n\n### Creation of instances during runtime\nExample 1: Tabs that use a jQuery plugin:\n\n```javascript\n$('.tabs').tabs();\n```\n\nWhile this works fine for the markup that is present at the time the website or web app is loaded, dealing with dynamic markup is more complicated. Every time new DOM nodes are loaded into the document one must take care that instances for those new nodes are also created.\n\nExample 2: new markup is loaded into the page:\n\n```javascript\n$.get('/server/foo/bar', function(markup) {\n  $('#theTargetNode')\n    .html(markup)\n    .find('.tabs').tabs();\n});\n```\n\nThis approach is problematic because \n\n  1. should one just query for every plugin that is known to the system?\n  2. should the little snippet **know** what kind of plugins are used in the response?\n\nThese questions show the problem: in this case the separation of concerns is violated! Every piece of code that deals with loading markup must either know what plugins are expected in the result or know the complete list of plugins used on the page.\n\nLoading data from the server is not the only place where the markup is changed. JavaScript templates are often used to create HTML markup during runtime. \n\n### Destroying instances during runtime\nMany components need a destructor to clean up variables or unbind from events to avoid memory leaks. This makes our little example even more complicated:\n\nExample 3: load markup and clean up before the new markup is added:\n\n```javascript\n$.get('/server/foo/bar', function(markup) {\n  $('#theTargetNode')\n    .find('.tabs').tabs('destroy');\n\n  $('#theTargetNode')\n    .html(markup)\n    .find('.tabs').tabs();\n});\n```\n\nNot only must the little snippet know all about the components that are use, but also about the destructors and how to call them.\n\n## Creating and destroying instances \"automagically\"\n\nComponentManager solves all these problems by creating instances for DOM nodes as soon as they are inserted into the DOM. It also takes care of calling the destructor functions when a DOM node is removed from the DOM.\n\nComponentManager has zero dependencies and uses a universal JavaScript module to be used in any environment. It depends on `MutationOberserver` which is supported by all modern browser.\n\n### Registering a component\n\nExample 4: Register a new component and provide callback for creating and removing instances:\n\n```javascript\nvar createTab = function(node) {\n  $(node).tabs();\n};\n\nvar removeTab = function(node) {\n  $(node).tabs('destroy');\n}\n\nComponentManager.register('tabs', createTab, removeTab);\n```\n\nEvery time a DOM node with the data attribute configuration `data-component-name=\"tabs\"` is added to the DOM, `createTab()` is executed with this node as parameter. As soon as a DOM node with this attribute and value is removed, `removeTabs()` (with the node as payload).\n\n### Bringing it all together\n\nExample 5: Registering a component and using it\n\n```javascript\nvar createTab = function(node) {\n  $(node).tabs();\n};\n\nvar removeTab = function(node) {\n  $(node).tabs('destroy');\n}\n\nComponentManager.register('tabs', createTab, removeTab);\n\n$.get('/server/foo/bar', function(markup) {\n  $('#theTargetNode').html(markup);\n});\n```\n\nWe've successfully decoupled loading markup into the DOM from the creation of instances of the jQuery tab plugin. The small snippet that loads markup into the page doesn't need to know about components and the ComponentManager doesn't need to know any specifics about how a component works.\nAny specific code can, of course, be used in the callbacks.\n\nExample 6: How to use component-specific code in a callback:\n\n```javascript\nvar createTab = function(node) {\n  var $node = $(node);\n  var options = $node.data('options');\n\n  $node.tabs(options);\n};\n```\n\n## Prioritisation of component initialisation\nSometimes one component depends on another and therefore they should be initialised in a specific manner. If you need a specific order you can use the optional fourth argument when registering a component. A lower number indicates a higher priority.\n\n\nExample 7: Components can have a priority when they are registered\n\n```javascript\nComponentManager.register('tabs', createTab, removeTab, 10);\nComponentManager.register('gmaps', createGmap, removeGmap, 20);\n```\n\nYou can also have a look at the second demo to see this in action.\n\n## Getting started\n\nThere are four ways to use ComponentManager.\n\n1. Installation via bower: `bower install component-manager`\n2. Installation via NPM: `npm install verwalter`\n3. Load the minified file via [unpkg.com](https://unpkg.com/verwalter/dist/manager.min.js) `\u003cscript src=\"https://unpkg.com/verwalter/dist/manager.min.js\"\u003e\u003c/script\u003e`\n4. Download the [minified file](https://raw.githubusercontent.com/jonathanweiss/component-manager/master/dist/manager.min.js) and store it locally: `\u003cscript src=\"manager.min.js\"\u003e\u003c/script\u003e`\n\n## Gotchas\nPlease note that due to the nature of `MutationObserver` the callbacks for creating and removing instances will be called asynchronously.\n\nExample 8: Instances are used too early:\n\n```javascript\n// see Example 5 for the code of the callbacks\nComponentManager.register('tabs', createTab, removeTab);\n\n$.get('/server/foo/bar', function(markup) {\n  $('#theTargetNode').html(markup);\n  $('#theTargetNode .tabs').eq(0).tabs('load', '#foo'); // \u003c-Uncaught TypeError: $(...).tabs is not a function\n});\n```\n\nThere are many solutions to wait for the `MutationObserver` to finish working before using the instances. They range from a (somewhat sloppy) call of `setTimeout(callback, 0)` to using events.\n\nExample 9: Wait for event before using instances\n\n```javascript\nvar createTab = function(node) {\n  $(node)\n    .tabs()\n    .trigger('TabCreated');\n};\n\nvar removeTab = function(node) {\n  $(node).tabs('destroy');\n}\n\nComponentManager.register('tabs', createTab, removeTab);\n\n$.get('/server/foo/bar', function(markup) {\n  $('#theTargetNode')\n    .html(markup)\n    .one('TabCreated', '.tabs', function() {\n      $('#theTargetNode .tabs').eq(0).tabs('load', '#foo');\n    });\n});\n```\n\n\n## Known issues\nIn IE11 the list of child nodes is empty when nodes are removed when the property `innerHTML` is set. This is a [known issue in IE11](https://connect.microsoft.com/IE/feedback/details/817132/ie-11-childnodes-are-missing-from-mutationobserver-mutations-removednodes-after-setting-innerhtml) that won't be fixed.\nIt prevents the callback for remove from being executed. If you need to support IE11 and use the remove callback be sure to include a [polyfill](https://github.com/talee/mutationobserver-polyfill) for `MutationObserver`.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjonathanweiss%2Fcomponent-manager","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjonathanweiss%2Fcomponent-manager","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjonathanweiss%2Fcomponent-manager/lists"}