├── .editorconfig ├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE.md ├── README.md ├── index.js └── package.json /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | indent_style = tab 7 | insert_final_newline = true 8 | trim_trailing_whitespace = true 9 | 10 | [*.md] 11 | trim_trailing_whitespace = false 12 | 13 | [*.{json,md,yml}] 14 | indent_size = 2 15 | indent_style = space 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | package-lock.json 3 | *.log* 4 | .* 5 | !.editorconfig 6 | !.gitignore 7 | !.rollup.js 8 | !.tape.js 9 | !.travis.yml 10 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - stable 4 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changes to Element QSA Scope 2 | 3 | ### 1.1.0 (July 5, 2018) 4 | 5 | - Polyfill `matches` and `closest` 6 | 7 | ### 1.0.1 (January 24, 2017) 8 | 9 | - Update project configuration 10 | 11 | ### 1.0.0 (October 13, 2016) 12 | 13 | - Initial release 14 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to Element QSA Scope 2 | 3 | You want to help? You rock! Now, take a moment to be sure your contributions 4 | make sense to everyone else. 5 | 6 | ## Reporting Issues 7 | 8 | Found a problem? Want a new feature? 9 | 10 | - See if your issue or idea has [already been reported]. 11 | - Provide a [reduced test case] or a [live example]. 12 | 13 | Remember, a bug is a _demonstrable problem_ caused by _our_ code. 14 | 15 | ## Submitting Pull Requests 16 | 17 | Pull requests are the greatest contributions, so be sure they are focused in 18 | scope and avoid unrelated commits. 19 | 20 | 1. To begin; [fork this project], clone your fork, and add our upstream. 21 | ```bash 22 | # Clone your fork of the repo into the current directory 23 | git clone git@github.com:YOUR_USER/element-qsa-scope.git 24 | 25 | # Navigate to the newly cloned directory 26 | cd element-qsa-scope 27 | 28 | # Assign the original repo to a remote called "upstream" 29 | git remote add upstream git@github.com:jonathantneal/element-qsa-scope.git 30 | 31 | # Install the tools necessary for testing 32 | npm install 33 | ``` 34 | 35 | 2. Create a branch for your feature or fix: 36 | ```bash 37 | # Move into a new branch for your feature 38 | git checkout -b feature/thing 39 | ``` 40 | ```bash 41 | # Move into a new branch for your fix 42 | git checkout -b fix/something 43 | ``` 44 | 45 | 3. If your code follows our practices, then push your feature branch: 46 | ```bash 47 | # Test current code 48 | npm test 49 | ``` 50 | ```bash 51 | # Push the branch for your new feature 52 | git push origin feature/thing 53 | ``` 54 | ```bash 55 | # Or, push the branch for your update 56 | git push origin update/something 57 | ``` 58 | 59 | That’s it! Now [open a pull request] with a clear title and description. 60 | 61 | [already been reported]: https://github.com/jonathantneal/create-postcss-plugin/issues 62 | [fork this project]: https://github.com/jonathantneal/create-postcss-plugin/fork 63 | [live example]: https://codepen.io/pen 64 | [open a pull request]: https://help.github.com/articles/using-pull-requests/ 65 | [reduced test case]: https://css-tricks.com/reduced-test-cases/ 66 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # CC0 1.0 Universal License 2 | 3 | Public Domain Dedication 4 | 5 | The person(s) who associated a work with this deed has dedicated the work to the public domain by waiving all of his or her rights to the work worldwide under copyright law, including all related and neighboring rights, to the extent allowed by law. 6 | 7 | You can copy, modify, distribute and perform the work, even for commercial purposes, all without asking permission. 8 | 9 | In no way are the patent or trademark rights of any person affected by CC0, nor are the rights that other persons may have in the work or in how the work is used, such as publicity or privacy rights. 10 | 11 | Unless expressly stated otherwise, the person(s) who associated a work with this deed makes no warranties about the work, and disclaims liability for all uses of the work, to the fullest extent permitted by applicable law. 12 | 13 | When using or citing the work, you should not imply endorsement by the author or the affirmer. 14 | 15 | This is a [human-readable summary of the Legal Code](//creativecommons.org/publicdomain/zero/1.0/) ([read the full text](//creativecommons.org/publicdomain/zero/1.0/)). 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Element QSA Scope [][Element QSA Scope] 2 | 3 | [![NPM Version][npm-img]][npm-url] 4 | [![Build Status][cli-img]][cli-url] 5 | [![Licensing][lic-img]][lic-url] 6 | [![Changelog][log-img]][log-url] 7 | [![Gitter Chat][git-img]][git-url] 8 | 9 | [Element QSA Scope] lets you select elements relative to the current node. It 10 | is a polyfill for `:scope` support within the selector methods 11 | [`Element#querySelector`], [`Element#querySelectorAll`], [`Element#matches`], 12 | and [`Element#closest`]. 13 | 14 | ```bash 15 | npm install element-qsa-scope 16 | ``` 17 | 18 | The entire script is 341 bytes when minified and gzipped. 19 | 20 | ### How it works 21 | 22 | By default, selector methods return elements matching CSS selectors absolutely 23 | from the document, similar to an element being matched with [`Element#matches`]. 24 | Imagine the following document: 25 | 26 | ```html 27 | 34 | 35 | ``` 36 | 37 | Without `:scope`, the following selector will return the first link. 38 | 39 | ```js 40 | li.querySelector('li a'); // returns Link 41 | ``` 42 | 43 | This is an example of how scoping is useful when traversing the DOM. 44 | 45 | ```js 46 | li.querySelector(':scope li a'); // returns Sublink 47 | ``` 48 | 49 | ## Browser compatibility 50 | 51 | | Browser | Native Support | Polyfill Support | 52 | | ----------------- | -------------- | ---------------- | 53 | | Android | 4.4+ | 2.2 - 4.2 | 54 | | Blackberry | ✘ | 7+ | 55 | | Chrome | 27+ | 4 - 26 | 56 | | Edge | ✘ | 12+ | 57 | | Firefox | 32+ | 3.5 - 31 | 58 | | Internet Explorer | ✘ | 8 - 11 | 59 | | Opera | 15+ | 10 - 14 | 60 | | Opera Mini | ✘ | 5+ | 61 | | Safari (iOS) | 7+ | 3.2 - 6 | 62 | | Safari (MacOS) | 6.2+ | 4 - 6 | 63 | 64 | ### Test Results 65 | 66 | Additional 67 | [tests](http://codepen.io/jonneal/details/e20f8ddced5f5ed111a1e1f3044d4d47) 68 | were run against the latest 3 versions of browsers, including common outliers 69 | such as Internet Explorer 8+ and Safari 6+. 70 | 71 | [Native Support](https://app.crossbrowsertesting.com/public/i76b092cd2b52b86/screenshots/zfe6755a18f932b6386b?size=small&type=chromeless) · [Polyfilled Support](https://app.crossbrowsertesting.com/public/i76b092cd2b52b86/screenshots/z60b7c71da7f3f060ff6?size=small&type=chromeless) 72 | 73 | [npm-url]: https://www.npmjs.com/package/element-qsa-scope 74 | [npm-img]: https://img.shields.io/npm/v/element-qsa-scope.svg 75 | [cli-url]: https://travis-ci.org/jonathantneal/element-qsa-scope 76 | [cli-img]: https://img.shields.io/travis/jonathantneal/element-qsa-scope.svg 77 | [lic-url]: LICENSE.md 78 | [lic-img]: https://img.shields.io/npm/l/element-qsa-scope.svg 79 | [log-url]: CHANGELOG.md 80 | [log-img]: https://img.shields.io/badge/changelog-md-blue.svg 81 | [git-url]: https://gitter.im/jonathantneal/element-qsa-scope 82 | [git-img]: https://img.shields.io/badge/chat-gitter-blue.svg 83 | 84 | [Element QSA Scope]: http://github.com/jonathantneal/element-qsa-scope 85 | 86 | [`Element#closest`]: https://dom.spec.whatwg.org/#dom-element-closest 87 | [`Element#matches`]: https://dom.spec.whatwg.org/#dom-element-matches 88 | [`Element#querySelector`]: https://dom.spec.whatwg.org/#dom-parentnode-queryselector 89 | [`Element#querySelectorAll`]: https://dom.spec.whatwg.org/#dom-parentnode-queryselectorall 90 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | try { 2 | // test for scope support 3 | document.querySelector(':scope *'); 4 | } catch (error) { 5 | (function (ElementPrototype) { 6 | // scope regex 7 | var scope = /:scope(?![\w-])/gi; 8 | 9 | // polyfill Element#querySelector 10 | var querySelectorWithScope = polyfill(ElementPrototype.querySelector); 11 | 12 | ElementPrototype.querySelector = function querySelector(selectors) { 13 | return querySelectorWithScope.apply(this, arguments); 14 | }; 15 | 16 | // polyfill Element#querySelectorAll 17 | var querySelectorAllWithScope = polyfill(ElementPrototype.querySelectorAll); 18 | 19 | ElementPrototype.querySelectorAll = function querySelectorAll(selectors) { 20 | return querySelectorAllWithScope.apply(this, arguments); 21 | }; 22 | 23 | // polyfill Element#matches 24 | if (ElementPrototype.matches) { 25 | var matchesWithScope = polyfill(ElementPrototype.matches); 26 | 27 | ElementPrototype.matches = function matches(selectors) { 28 | return matchesWithScope.apply(this, arguments); 29 | }; 30 | } 31 | 32 | // polyfill Element#closest 33 | if (ElementPrototype.closest) { 34 | var closestWithScope = polyfill(ElementPrototype.closest); 35 | 36 | ElementPrototype.closest = function closest(selectors) { 37 | return closestWithScope.apply(this, arguments); 38 | }; 39 | } 40 | 41 | function polyfill(qsa) { 42 | return function (selectors) { 43 | // whether the selectors contain :scope 44 | var hasScope = selectors && scope.test(selectors); 45 | 46 | if (hasScope) { 47 | // fallback attribute 48 | var attr = 'q' + Math.floor(Math.random() * 9000000) + 1000000; 49 | 50 | // replace :scope with the fallback attribute 51 | arguments[0] = selectors.replace(scope, '[' + attr + ']'); 52 | 53 | // add the fallback attribute 54 | this.setAttribute(attr, ''); 55 | 56 | // results of the qsa 57 | var elementOrNodeList = qsa.apply(this, arguments); 58 | 59 | // remove the fallback attribute 60 | this.removeAttribute(attr); 61 | 62 | // return the results of the qsa 63 | return elementOrNodeList; 64 | } else { 65 | // return the results of the qsa 66 | return qsa.apply(this, arguments); 67 | } 68 | }; 69 | } 70 | })(Element.prototype); 71 | } 72 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "element-qsa-scope", 3 | "version": "1.1.0", 4 | "description": "Return elements matching a selector relative to the current node", 5 | "author": "Jonathan Neal ", 6 | "license": "CC0-1.0", 7 | "repository": "jonathantneal/element-qsa-scope", 8 | "homepage": "https://github.com/jonathantneal/element-qsa-scope#readme", 9 | "bugs": "https://github.com/jonathantneal/element-qsa-scope/issues", 10 | "main": "index.js", 11 | "files": [ 12 | "index.js" 13 | ], 14 | "scripts": { 15 | "lint": "npm run lint:ec && npm run lint:js", 16 | "lint:ec": "echint", 17 | "lint:js": "eslint *.js --cache --ignore-pattern .gitignore", 18 | "prepublish": "npm test", 19 | "test": "npm run lint" 20 | }, 21 | "devDependencies": { 22 | "echint": "^4.0.1", 23 | "eslint": "^5.0.1", 24 | "eslint-config-dev": "^2.0.0" 25 | }, 26 | "eslintConfig": { 27 | "extends": "dev", 28 | "rules": { 29 | "no-unused-vars": [ 30 | "error", 31 | { 32 | "argsIgnorePattern": "^selectors$" 33 | } 34 | ] 35 | } 36 | }, 37 | "keywords": [ 38 | "scope", 39 | "dom", 40 | "element", 41 | "matches", 42 | "polyfill", 43 | "standard", 44 | "relative", 45 | "context", 46 | "queries", 47 | "query", 48 | "selectors" 49 | ] 50 | } 51 | --------------------------------------------------------------------------------