Skip to content

Commit

Permalink
feat: add nth-of-type selector type
Browse files Browse the repository at this point in the history
  • Loading branch information
fczbkk committed Dec 28, 2018
1 parent 8572e72 commit e8c5e01
Show file tree
Hide file tree
Showing 7 changed files with 87 additions and 13 deletions.
16 changes: 8 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ Then include it in your source code:

```javascript
import getCssSelector from 'css-selector-generator';
```
```

## How to use

Expand Down Expand Up @@ -229,13 +229,13 @@ Type: [Object](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Globa
- `whitelist` **[Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)<([RegExp](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/RegExp) \| [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String))>?** List of selectors that should be prioritised.
- `blacklist` **[Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)<([RegExp](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/RegExp) \| [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String))>?** List of selectors that should be ignored.
- `root` **[Element](https://developer.mozilla.org/docs/Web/API/Element)?** Root element inside which the selector will be generated. If not set, the document root will be used.
- `combineWithinSelector` **[boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?** If set to `false`, the generator will not test combinations of selectors of single type (e.g. multiple class selectors).
- `combineBetweenSelectors` **[boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?** If set to `true`, the generator will try to test combinations of selectors of different types. Warning: Turning this on may cause performance issues.
- `combineWithinSelector` **[boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?** If set to `true`, the generator will test combinations of selectors of single type (e.g. multiple class selectors).
- `combineBetweenSelectors` **[boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?** If set to `true`, the generator will try to test combinations of selectors of different types (e.g. tag + class name).
- `includeTag` **[boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?** If set to `true`, all generated selectors will include the TAG part. Even if tag selector type is not included in `selectors` option.

### css_selector_type

Type: (`"id"` \| `"class"` \| `"tag"` \| `"attribute"` \| `"nthchild"`)
Type: (`"id"` \| `"class"` \| `"tag"` \| `"attribute"` \| `"nthchild"` \| `"nthoftype"`)

## Migrate from v1 to v2

Expand All @@ -250,10 +250,10 @@ mySelectorGenerator.getSelector(elementReference);
getCssSelector(elementReference, {/* custom options */});
```

- Options `id_blacklist`, `class_blacklist` and `attribute_blacklist` are replaced with single `blacklist` option, which is now applied to all selector types.
- Option `attribute_whitelist` is replaced with `whitelist` option, which is now applied to all selector types.
- Option `prefix_tag` is renamed to `includeTag`.
- Option `quote_attribute_when_needed` is removed. The attribute selectors are quoted automatically.
- Options `id_blacklist`, `class_blacklist` and `attribute_blacklist` are replaced with single `blacklist` option, which is now applied to all selector types.
- Option `attribute_whitelist` is replaced with `whitelist` option, which is now applied to all selector types.
- Option `prefix_tag` is renamed to `includeTag`.
- Option `quote_attribute_when_needed` is removed. The attribute selectors are quoted automatically.

## Bug reports, feature requests and contact

Expand Down
9 changes: 8 additions & 1 deletion src/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,11 @@ export const INVALID_ID_RE = new RegExp([
].join('|'));

// Order in which a combined selector is constructed.
export const SELECTOR_PATTERN = ['tag', 'id', 'class', 'attribute', 'nthchild'];
export const SELECTOR_PATTERN = [
'nthoftype',
'tag',
'id',
'class',
'attribute',
'nthchild'
];
2 changes: 1 addition & 1 deletion src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,5 +42,5 @@ export default getCssSelector;
*/

/**
* @typedef {'id' | 'class' | 'tag' | 'attribute' | 'nthchild'} css_selector_type
* @typedef {'id' | 'class' | 'tag' | 'attribute' | 'nthchild' | 'nthoftype'} css_selector_type
*/
18 changes: 18 additions & 0 deletions src/selector-nth-of-type.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import {getTagSelector} from './selector-tag';

/**
* Get nth-of-type selector for an element.
* @param {Element} element
* @return {selectors_list}
*/
export function getNthOfTypeSelector (element) {
const tag = getTagSelector(element)[0];
const siblings = element.parentElement.querySelectorAll(tag);
for (let i = 0; i < siblings.length; i++) {
if (siblings[i] === element) {
return [`${tag}:nth-of-type(${i + 1})`];
}
}

return [];
}
4 changes: 3 additions & 1 deletion src/utilities-selectors.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {getTagSelector} from './selector-tag';
import {getIdSelector} from './selector-id';
import {getClassSelectors} from './selector-class';
import {getAttributeSelectors} from './selector-attribute';
import {getNthOfTypeSelector} from './selector-nth-of-type';

export const ESCAPED_COLON = ':'
.charCodeAt(0)
Expand Down Expand Up @@ -47,6 +48,7 @@ export const SELECTOR_TYPE_GETTERS = {
class: getClassSelectors,
attribute: getAttributeSelectors,
nthchild: getNthChildSelector,
nthoftype: getNthOfTypeSelector,
};

/**
Expand Down Expand Up @@ -162,7 +164,7 @@ export function getSelectorsToGet (options) {
* @return {Array.<string>}
*/
function addTagTypeIfNeeded (list) {
return list.includes('tag')
return (list.includes('tag') || list.includes('nthoftype'))
? [...list]
: [...list, 'tag'];
}
Expand Down
8 changes: 6 additions & 2 deletions test/selector-nth-child.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,15 @@ describe('selector - nth-child', function () {

it('should generate nth-child selector', function () {
root.innerHTML = '<div></div><div></div>';
assert.equal(getNthChildSelector(root.lastChild), ':nth-child(2)');
const result = getNthChildSelector(root.lastChild);
assert.equal(result.length, 1);
assert.equal(result[0], ':nth-child(2)');
});

it('should generate for BODY', function () {
assert.equal(getNthChildSelector(document.body), ':nth-child(2)');
const result = getNthChildSelector(document.body);
assert.equal(result.length, 1);
assert.equal(result[0], ':nth-child(2)');
});

});
43 changes: 43 additions & 0 deletions test/selector-nth-of-type.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import {assert} from 'chai';
import {getNthOfTypeSelector} from '../src/selector-nth-of-type';
import {getCssSelector} from '../src';

describe('selector - nth-of-type', function () {

let root;

beforeEach(function () {
root = document.body.appendChild(document.createElement('div'));
});

afterEach(function () {
root.parentNode.removeChild(root);
});

it('should generate nth-child selector', function () {
root.innerHTML = '<div></div><p></p><p></p>';
const result = getNthOfTypeSelector(root.lastChild);
assert.equal(result.length, 1);
assert.equal(result[0], 'p:nth-of-type(2)');
});

it('should not collide with tag selector', function () {
root.innerHTML = '<div></div><p></p><p></p>';
const result = getCssSelector(root.lastChild, {
selectors: ['tag', 'nthoftype'],
root,
});
assert.equal(result, 'p:nth-of-type(2)');
});

it('should not collide with `includeTag` option', function () {
root.innerHTML = '<div></div><p></p><p></p>';
const result = getCssSelector(root.lastChild, {
selectors: ['nthoftype'],
includeTag: true,
root,
});
assert.equal(result, 'p:nth-of-type(2)');
});

});

0 comments on commit e8c5e01

Please sign in to comment.