├── .gitignore ├── .gitmodules ├── README.md ├── config └── webpack.medv.finder.js ├── index.html ├── package-lock.json ├── package.json ├── test.js └── vendor └── snowflake └── index.js /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules/ 2 | /.idea/ 3 | /temp/ 4 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "vendor/requirejs"] 2 | path = vendor/requirejs 3 | url = git@github.com:jrburke/requirejs.git 4 | [submodule "vendor/selector-generator"] 5 | path = vendor/selector-generator 6 | url = git@github.com:tildeio/selector-generator.git 7 | [submodule "vendor/dompath"] 8 | path = vendor/dompath 9 | url = git@github.com:jhartikainen/dompath.git 10 | [submodule "vendor/CSSelector.js"] 11 | path = vendor/CSSelector.js 12 | url = git@github.com:stevoland/CSSelector.js.git 13 | [submodule "vendor/ellocate.js"] 14 | path = vendor/ellocate.js 15 | url = git@github.com:bimech/ellocate.js.git 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CSS Selector Generator Benchmark 2 | 3 | This is an attempt to create a benchmark for various JavaScript libraries for generating CSS selectors. It is inspired by @dandv's [question](https://github.com/fczbkk/css-selector-generator/issues/2). 4 | 5 | ## Usage 6 | 7 | ```sh 8 | npm install 9 | npm test 10 | ``` 11 | 12 | `index.html` should open in a browser tab with further instructions. 13 | 14 | ## Results 15 | 16 | ### @antonmedv [finder](https://github.com/antonmedv/finder) 17 | 18 | * NPM package, written in TypeScript 19 | * no dependencies 20 | * has tests 21 | * has documentation 22 | * MIT license 23 | * slower speed than other libraries (still fast enough for regular use) 24 | * creates efficient and robust selectors using ID, class, tags and child marker 25 | * does not seem to support attribute selectors 26 | * generates **shortest selectors** among all tested libraries 27 | 28 | Longest selector: 29 | 30 | ``` 31 | .block:nth-child(3) li:nth-child(2) > .icon-eye-open 32 | ``` 33 | 34 | ### @autarc [optimal-select](https://github.com/autarc/optimal-select) 35 | 36 | * supports UMD (Browser & Node) 37 | * no dependencies 38 | * no tests 39 | * MIT license 40 | * allows single and multiple input elements 41 | * separate handling of selection and optimization (export ES2015 Modules) 42 | * creates efficient and robust selectors using ID, class, attributes, tags and child marker 43 | 44 | Longest selector: 45 | 46 | ``` 47 | .clearfix:nth-of-type(3) li:nth-of-type(2) .icon-eye-open 48 | ``` 49 | 50 | 51 | ### @bimech [ellocate.js](https://github.com/bimech/ellocate.js) 52 | 53 | * supports Bower 54 | * depends on Jquery 55 | * has tests 56 | * has documentation 57 | * no license 58 | * average speed 59 | * uses ID, class and tag selectors 60 | * **WARNING:** doesn't use `nth-child` selectors, so it **produces a lot of non-unique selectors** 61 | 62 | Longest selector: 63 | 64 | ``` 65 | html > body > div > div#wrap > div#main > div.container > div.main-content > div.row > div.span12 > div.row > div.span4.sidebar > div.block.clearfix > div.block-header.clearfix > div.block-action > a.btn.btn-success.btn-small > i.icon-plus.icon-white 66 | ``` 67 | 68 | ### Chromium's [DOMPresentationUtils](https://chromium.googlesource.com/chromium/blink/+/master/Source/devtools/front_end/components/DOMPresentationUtils.js) 69 | 70 | NOTE: Used [version on NPM](https://www.npmjs.com/package/cssman) adapted for use in browser. 71 | 72 | * supports NPM 73 | * no dependencies 74 | * no tests 75 | * has documentation 76 | * see source code for license 77 | * average speed 78 | * uses ID, class, tag, attribute (for inputs) and `nth-child` child selectors 79 | * **WARNING: produces a lot of non-unique selectors** in both optimized and non-optimized version 80 | 81 | Example of non-unique selector: 82 | 83 | ``` 84 | div#main > div > div > div > div > div > div.span4.sidebar > div.block.clearfix > div.block-content > ul > li.show-all > a 85 | 86 | [ 87 | ​Show all​​, 88 | ​Show all​​, 89 | ​Show all​​, 90 | ​Show all​​ 91 | ] 92 | ``` 93 | 94 | Longest selector: 95 | 96 | ``` 97 | div#main > div > div > div > div > div > div.span4.sidebar > div.block.clearfix > div.block-content > ul > li:nth-child(1) > a 98 | ``` 99 | 100 | ### @desmondw [snowflake](https://github.com/desmondw/snowflake) 101 | 102 | This is a Chrome extension, not a stand-alone library. 103 | 104 | * average speed 105 | * uses combination of tag and class or `nth-child` 106 | 107 | Longest selector: 108 | 109 | ``` 110 | div.span12 > div:nth-of-type(1) > div:nth-of-type(1) > ul:nth-of-type(1) > li:nth-of-type(10) > div:nth-of-type(1) > div:nth-of-type(2) > span:nth-of-type(1) 111 | ``` 112 | 113 | ### @fczbkk [css-selector-generator](https://github.com/fczbkk/css-selector-generator) 114 | 115 | * supports Bower and NPM 116 | * no dependencies 117 | * has tests 118 | * has documentation 119 | * Unlicense license 120 | * tries to use optimized ID, class, tag child selectors or their combination, uses `nth-child` as fallback 121 | 122 | Longest selector: 123 | 124 | ``` 125 | .span12 > :nth-child(1) > .span8 > ul > :nth-child(1) > :nth-child(1) 126 | ``` 127 | 128 | ### @jhartikainen [dompath](https://github.com/jhartikainen/dompath) 129 | 130 | * no support for Bower or NPM 131 | * no dependencies 132 | * has tests 133 | * has documentation 134 | * no license 135 | * very fast 136 | * uses ID or tagg + `nth-child` child selector, so the selectors tend to become quite long 137 | 138 | Longest selector: 139 | 140 | ``` 141 | #main > div:nth-child(1) > div:nth-child(1) > div:nth-child(1) > div:nth-child(1) > div:nth-child(1) > div:nth-child(1) > ul:nth-child(2) > li:nth-child(10) > div:nth-child(2) > div:nth-child(2) > span:nth-child(4) 142 | ``` 143 | 144 | ### @martinsbalodis [css-selector](https://github.com/martinsbalodis/css-selector) 145 | 146 | Sorry, I wasn't able to make it work. 147 | 148 | 149 | ### @ngs [jquery-selectorator](https://github.com/ngs/jquery-selectorator) 150 | 151 | * supports NPM and Bower 152 | * depends on Jquery 153 | * has tests 154 | * has documentation 155 | * MIT license 156 | * **very slow** 157 | * generates selectors using Jquery's `:eq()` selector, so most of the results are not valid CSS selectors and are only usable within Jquery 158 | 159 | Longest selector: n/a 160 | 161 | 162 | ### @olivierrr [selector-query](https://github.com/olivierrr/selector-query) 163 | 164 | * supports NPM only 165 | * no dependencies 166 | * no tests 167 | * has documentation 168 | * MIT license 169 | * quite fast 170 | * generates the [most complex descendant selector](https://github.com/olivierrr/selector-query/issues/1#issuecomment-133116659) for each element (ID, class, tag, `nth-child`), so it produces the **longest selectors** among tested libraries 171 | * **WARNING:** uses descendant selectors instead of child selectors, so it sometimes **produces non-unique selectors** 172 | 173 | Longest selector: 174 | 175 | ``` 176 | #main div.container:nth-child(1) div.main-content:nth-child(1) div.row:nth-child(1) div.span12:nth-child(1) div.row:nth-child(1) div.span4.sidebar:nth-child(2) div.block.clearfix:nth-child(2) div.block-header.clearfix:nth-child(1) div.block-action:nth-child(2) a.btn.btn-success.btn-small:nth-child(1) i.icon-plus.icon-white:nth-child(1) 177 | ``` 178 | 179 | ### @rishihahs [domtalk](https://github.com/rishihahs/domtalk) 180 | 181 | * supports NPM only 182 | * no dependencies 183 | * has tests 184 | * has documentation 185 | * MIT license 186 | * very fast 187 | * uses ID or `nth-child` descendant selector, selectors are of average length 188 | * **WARNING:** uses descendant selectors instead of child selectors, so it **produces a lot of non-unique selectors** 189 | 190 | Longest selector: 191 | 192 | ``` 193 | #wrap *:nth-child(1) *:nth-child(1) *:nth-child(1) *:nth-child(3) *:nth-child(1) *:nth-child(1) *:nth-child(1) *:nth-child(3) *:nth-child(11) *:nth-child(1) *:nth-child(1) 194 | ``` 195 | 196 | ### @stevoland [CSSelector.js](https://github.com/stevoland/CSSelector.js) 197 | 198 | * supports NPM (claims to support Bower, but I could not find it in the registry) 199 | * supports AMD 200 | * no dependencies 201 | * no tests 202 | * has documentation 203 | * MIT license 204 | * very fast 205 | * uses ID or tag + `nth-child` child selectors, selectors are quite long 206 | 207 | Longest selector: 208 | 209 | ``` 210 | #main > DIV:nth-child(1) > DIV:nth-child(1) > DIV:nth-child(1) > DIV:nth-child(1) > DIV:nth-child(1) > DIV:nth-child(1) > UL:nth-child(2) > LI:nth-child(10) > DIV:nth-child(2) > DIV:nth-child(2) > SPAN:nth-child(4) 211 | ``` 212 | 213 | ### @thomaspeklak [get-query-selector](https://github.com/thomaspeklak/get-query-selector) 214 | 215 | * supports NPM only 216 | * no dependencies 217 | * no tests 218 | * has documentation 219 | * looks like BSD license 220 | * very fast 221 | * uses ID or `nth-child` child selector, selectors are of average length 222 | 223 | Longest selector: 224 | 225 | ``` 226 | #wrap>:nth-child(1)>:nth-child(1)>:nth-child(1)>:nth-child(3)>:nth-child(1)>:nth-child(1)>:nth-child(1)>:nth-child(3)>:nth-child(11)>:nth-child(1)>:nth-child(1) 227 | ``` 228 | 229 | ### @tildeio [selector-generator](https://github.com/tildeio/selector-generator) 230 | 231 | * no NPM or Bower 232 | * requires RequireJS 233 | * has tests 234 | * no documentation 235 | * looks like MIT license 236 | * very fast 237 | * uses tag or tag + `nth-child` child selectors 238 | * **WARNING: produces a lot of non-unique selectors** 239 | 240 | Longest selector: 241 | 242 | ``` 243 | html > body > div > div > div > div > div > div > div > div > div:nth-of-type(2) > div:nth-of-type(2) > div:nth-of-type(2) > ul > li:nth-of-type(2) > a 244 | ``` 245 | 246 | 247 | ## TODO 248 | 249 | It would be nice to automate the process, run the tests in PhantomJS, etc. Pull requests are welcome. 250 | 251 | ## Bug reports, feature requests and contact 252 | 253 | If you found any bugs, if you have feature requests or any questions, please, either [file an issue at GitHub](https://github.com/fczbkk/css-selector-generator-benchmark/issues) or send me an e-mail at [riki@fczbkk.com](mailto:riki@fczbkk.com?subject=CSSSelectorGeneratorBenchmark) 254 | -------------------------------------------------------------------------------- /config/webpack.medv.finder.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | mode: 'production', 5 | entry: './node_modules/@medv/finder/dist/index.js', 6 | output: { 7 | path: path.resolve(__dirname, '../temp/finder/'), 8 | filename: 'index.js', 9 | library: 'finder', 10 | libraryTarget: 'var' 11 | } 12 | }; -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | CSS Selector Generators Benchmark 7 | 8 | 13 | 14 | 15 | 16 | 17 | 27 | 28 | 29 | 30 | 31 | 32 | 42 | 43 | 44 | 45 | 62 | 63 | 64 | 73 | 74 | 75 | 76 | 83 | 84 | 85 | 86 | 87 | 93 | 94 | 95 | 96 | 97 | 103 | 104 | 105 | 106 | 107 | 113 | 114 | 115 | 122 | 123 | 124 | 125 | 132 | 133 | 134 | 135 | 142 | 143 | 144 | 145 | 150 | 151 | 155 | 156 | 157 | 164 | 165 | 166 | 175 | 176 | 177 | 178 | 185 | 186 | 187 | 188 | 189 | 190 |

CSS Selector Generators Benchmark

191 | 192 |

This test attempts to compare various JS libraries for generating CSS selectors of HTML elements. Each library is optimized for something else, e.g. speed, browser compatibility, selector length, etc. If some library does not have good score in some 193 | category, it does not mean that the library is rubbish.

194 | 195 |

How it works

196 | 197 | 201 | 202 |

Results

203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 |
LibraryValidInvalidNot foundNon uniqueNot matchingLongest selectorDuration
221 | 222 | 223 |
224 |

Testing HTML code

225 | 226 |
227 | 228 | 318 | 319 | 320 |
321 |
322 |
323 | 324 |
325 |
326 | 327 | 328 |
329 |
330 | 331 |

What's happening

332 |
    333 |
  • 334 |

    335 |

    336 |
    337 | 338 |
    339 | 340 |
    341 | marek committed 3 times to 342 | Out of the box file handling 343 |
    344 | 4 days ago 345 |
    346 |
    347 |

    348 |
  • 349 |
  • 350 |

    351 |

    352 |
    353 | 354 |
    355 | 356 |
    357 | marek committed 12 times to 358 | test 2 359 |
    360 | 6 days ago 361 |
    362 |
    363 |

    364 |
  • 365 |
  • 366 |

    367 |

    368 |
    369 | 370 |
    371 | 372 |
    373 | marek committed 2 times to 374 | Out of the box file handling 375 |
    376 | 4 months ago 377 |
    378 |
    379 |

    380 |
  • 381 |
  • 382 |

    383 |

    384 |
    385 | 386 |
    387 | 388 |
    389 | marek committed to 390 | test moodle 391 |
    392 | 4 months ago 393 |
    394 |
    395 |

    396 |
  • 397 |
  • 398 |

    399 |

    400 |
    401 | 402 |
    403 | 404 |
    405 | marek committed 3 times to 406 | Out of the box file handling 407 |
    408 | 5 months ago 409 |
    410 |
    411 |

    412 |
  • 413 |
  • 414 |

    415 |

    416 |
    417 | 418 |
    419 | 420 |
    421 | marek cloned topic from 422 | test moodle 423 |
    424 | 5 months ago 425 |
    426 |
    427 |

    428 |
  • 429 |
  • 430 |

    431 |

    432 |
    433 | 434 |
    435 | 436 |
    437 | marek created new topic 438 | test moodle 439 |
    440 | 5 months ago 441 |
    442 |
    443 |

    444 |
  • 445 |
  • 446 |

    447 |

    448 |
    449 | 450 |
    451 | 452 |
    453 | marek committed 5 times to 454 | Out of the box file handling 455 |
    456 | 5 months ago 457 |
    458 |
    459 |

    460 |
  • 461 |
  • 462 |

    463 |

    464 |
    465 | 466 |
    467 | 468 |
    469 | marek created new topic 470 | Out of the box file handling 471 |
    472 | 6 months ago 473 |
    474 |
    475 |

    476 |
  • 477 |
  • 478 |

    479 |

    480 |
    481 | 482 |
    483 | 484 |
    485 | marek created new topic 486 | test 2 487 |
    488 | 6 months ago 489 |
    490 |
    491 |

    492 |
  • 493 |
  • 494 |

    495 |

    496 |
    497 | 498 |
    499 | 500 |
    501 | marek created new topic 502 | test 503 |
    504 | 9 months ago 505 |
    506 |
    507 |

    508 |
  • 509 |
  • 510 |

    511 |

    512 |
    513 | 514 |
    515 | 516 |
    517 | marek created new topic test 518 |
    519 | 9 months ago 520 |
    521 |
    522 |

    523 |
  • 524 | 525 |
526 | 527 | 528 | 529 |
530 | 531 | 640 | 641 |
642 | 643 | 644 |
645 |
646 |
647 | 648 |
649 | 650 |
651 | 652 |
653 |
654 | 655 | 656 | 678 | 679 |
680 | 681 | 682 | 683 | 684 | 685 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "css-selector-generator-benchmark", 3 | "version": "1.0.0", 4 | "description": "Benchmark for various JS libraries that generate unique CSS selectors.", 5 | "main": "index.html", 6 | "scripts": { 7 | "pretest": "git submodule update --init && npm run compile", 8 | "test": "index.html", 9 | "compile": "npm run compile:finder", 10 | "compile:finder": "webpack --config ./config/webpack.medv.finder.js" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "https://github.com/css-selector-generator-benchmark" 15 | }, 16 | "keywords": [ 17 | "javascript", 18 | "css", 19 | "benchmark" 20 | ], 21 | "author": "Riki Fridrich (http://fczbkk.com/)", 22 | "license": "MIT", 23 | "dependencies": { 24 | "@medv/finder": "^1.1.2", 25 | "css-selector": "^0.1.0", 26 | "css-selector-generator": "^1.2.0", 27 | "cssman": "0.0.2", 28 | "domtalk": "0.0.2", 29 | "get-query-selector": "0.0.1", 30 | "jquery": "^3.5.0", 31 | "jquery-selectorator": "^0.1.3", 32 | "optimal-select": "^4.0.1", 33 | "selector-query": "^1.0.1" 34 | }, 35 | "devDependencies": { 36 | "webpack": "^4.4.1", 37 | "webpack-cli": "^2.0.13" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | var libraries = {} 2 | 3 | 4 | function addLibrary(name, callback) { 5 | libraries[name] = callback; 6 | } 7 | 8 | 9 | function runTests() { 10 | 11 | results = {}; 12 | 13 | for (key in libraries) { 14 | var testFunction = libraries[key]; 15 | results[key] = getResults(testFunction); 16 | } 17 | 18 | drawResults(results); 19 | } 20 | 21 | 22 | function drawResults(results) { 23 | var wrapper = document.querySelector('#results'); 24 | wrapper.innerHTML = ''; 25 | 26 | var output = document.createDocumentFragment() 27 | 28 | var resultsList = Object.keys(results).map(function (key) { 29 | var result = results[key] 30 | result.key = key 31 | return result 32 | }) 33 | // priority: Valid (v), Longest (^), Duration (^) 34 | .sort(function(curr, next) { 35 | return next.validSelectors.length - curr.validSelectors.length || 36 | curr.longestSelector.length - next.longestSelector.length || 37 | curr.duration - next.duration 38 | }) 39 | 40 | resultsList.forEach(function(data) { 41 | var row = output.appendChild(document.createElement('tr')); 42 | addCell(row, data.key); 43 | addCell(row, data.validSelectors.length); 44 | addCell(row, data.invalidSelectors.length); 45 | addCell(row, data.notFoundSelectors); 46 | addCell(row, data.nonUniqueSelectors.length); 47 | addCell(row, data.nonMatchingSelectors.length); 48 | addCell(row, "(" + data.longestSelector.length + ") " + data.longestSelector); 49 | addCell(row, data.duration + "ms"); 50 | }) 51 | 52 | wrapper.appendChild(output); 53 | 54 | console.log(resultsList); 55 | } 56 | 57 | function hasInvalidSelectors (data) { 58 | return data.invalidSelectors.length || data.notFoundSelectors.length || 59 | data.nonUniqueSelectors.length || data.nonMatchingSelectors.length 60 | } 61 | 62 | function addCell(row, content) { 63 | var cell = row.appendChild(document.createElement('td')); 64 | cell.appendChild(document.createTextNode(content)); 65 | return cell; 66 | } 67 | 68 | 69 | function getResults(testFunction) { 70 | 71 | var elements = document.querySelector('#wrap').querySelectorAll('*'); 72 | 73 | var result = { 74 | duration: -1, 75 | validSelectors: [], 76 | invalidSelectors: [], 77 | nonUniqueSelectors: [], 78 | nonMatchingSelectors: [], 79 | notFoundSelectors: 0, 80 | longestSelector: '' 81 | }; 82 | var outputs = []; 83 | 84 | var timeStart = (new Date).getTime(); 85 | 86 | for (var i = 0, j = elements.length; i < j; i++) { 87 | var element = elements[i]; 88 | var selector = testFunction(element); 89 | outputs.push({ 90 | element: element, 91 | selector: selector 92 | }); 93 | } 94 | 95 | var timeEnd = (new Date).getTime(); 96 | result.duration = timeEnd - timeStart 97 | 98 | for (i = 0, j = outputs.length; i < j; i++) { 99 | var selector = outputs[i].selector; 100 | var element = outputs[i].element; 101 | 102 | if (selector) { 103 | 104 | var foundElements = [] 105 | 106 | try { 107 | foundElements = document.querySelectorAll(selector); 108 | } catch (e) { 109 | result.invalidSelectors.push(selector); 110 | } 111 | 112 | 113 | if (foundElements.length > 1) { 114 | result.nonUniqueSelectors.push(selector); 115 | } else { 116 | if (foundElements[0] === element) { 117 | result.validSelectors.push(selector); 118 | } else { 119 | result.nonMatchingSelectors.push(selector); 120 | } 121 | } 122 | 123 | if (selector.length > result.longestSelector.length) { 124 | result.longestSelector = selector; 125 | } 126 | 127 | } else { 128 | result.notFoundSelectors++; 129 | } 130 | 131 | } 132 | 133 | return result; 134 | } 135 | -------------------------------------------------------------------------------- /vendor/snowflake/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | code extracted from: 3 | https://github.com/desmondw/snowflake/blob/master/js/inject.js#L55 4 | */ 5 | 6 | 7 | 8 | 9 | 10 | // creates a unique css selector for the given element 11 | function generateSelector(el){ 12 | var selector = ""; 13 | var tree = $(el).parentsUntil(document); 14 | 15 | // generate full selector by traversing DOM from bottom-up 16 | for (var i = -1; i < tree.length; i++){ 17 | var e = i < 0 ? el : tree[i]; 18 | 19 | var eCSS = querifyElement(e); 20 | var query = eCSS.query + (selector.length ? ' > ' : '') + selector; 21 | 22 | var matches = $(query); 23 | 24 | if (matches.length === 1 && matches[0] === el){ 25 | return query; 26 | } 27 | else if (matches.length > 1 && i + 1 < tree.length){ 28 | 29 | var parentQuery = generateSelector(tree[i + 1]); 30 | var parentMatches = $(parentQuery).children(eCSS.tag); 31 | var nthQuery = eCSS.tag + ':nth-of-type(' + (parentMatches.index(el) + 1) + ')' + (selector.length ? ' > ' : '') + selector; 32 | var parentNthQuery = parentQuery + ' > ' + nthQuery; 33 | var nthMatches = $(parentNthQuery); 34 | 35 | if (nthMatches.length === 1 && nthMatches[0] === el){ 36 | return parentNthQuery; 37 | } 38 | else { 39 | printError("----------") 40 | return 'ERROR'; 41 | } 42 | } 43 | else { 44 | if (matches.length === 1) printError("Matched incorrect element. (matches.length = " + matches.length + ")") 45 | else if (matches.length > 1) printError("Multiple matches, but traversed entire tree (algorithm not being specific enough).") 46 | else printError("Could not find match for tag/id/class selector. (matches.length = " + matches.length + ")") 47 | return 'ERROR'; 48 | } 49 | } 50 | 51 | return selector; 52 | } 53 | 54 | 55 | 56 | // returns object with element information in query format 57 | function querifyElement(e){ 58 | if (!e) return null; 59 | 60 | var tag = e.tagName.toLowerCase(); 61 | var ids = e.id ? '#' + e.id.trim().split(' ').join('#') : ""; 62 | var classes = e.className ? '.' + e.className.trim().split(' ').join('.') : ""; 63 | var query = tag + ids + classes; 64 | 65 | return { 66 | element: e, 67 | tag: tag, 68 | ids: ids, 69 | classes: classes, 70 | query: query 71 | } 72 | } 73 | --------------------------------------------------------------------------------