als-document 0.1.1 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/document.js +489 -512
- package/package.json +6 -7
- package/parser/parser.js +287 -0
- package/parser/readme.md +121 -0
- package/parser/test.js +233 -0
- package/query/query.js +147 -0
- package/query/readme.md +131 -0
- package/query/test.js +143 -0
- package/readme.md +287 -135
- package/selector/readme.md +74 -0
- package/selector/selector.js +125 -0
- package/selector/test.js +410 -0
- package/test/html1.js +50 -0
- package/test/html2.js +1126 -0
- package/test/test.js +17 -0
package/readme.md
CHANGED
|
@@ -1,200 +1,352 @@
|
|
|
1
|
-
# Als-
|
|
1
|
+
# Als-document
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Builded from scatch and tested.
|
|
4
|
+
All tested with als-test.
|
|
4
5
|
|
|
6
|
+
Als-document is a library which includes 3 instrements:
|
|
7
|
+
* Html parser - for fast html string parsing and building dom tree
|
|
8
|
+
* Html query parser - for destructing css/html query to selectors inside array of objects
|
|
9
|
+
* Html selector - for selecting elements with css/html query inside parsed dom tree
|
|
5
10
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
11
|
+
- [Als-document](#als-document)
|
|
12
|
+
- [Selector](#selector)
|
|
13
|
+
- [Basics](#basics)
|
|
14
|
+
- [FrontEnd usage](#frontend-usage)
|
|
15
|
+
- [BackEnd usage](#backend-usage)
|
|
16
|
+
- [Extra abilities](#extra-abilities)
|
|
17
|
+
- [HtmlParser](#htmlparser)
|
|
18
|
+
- [Syntax](#syntax)
|
|
19
|
+
- [Frontend example](#frontend-example)
|
|
20
|
+
- [Backend example](#backend-example)
|
|
21
|
+
- [Query](#query)
|
|
22
|
+
- [Syntax](#syntax-1)
|
|
23
|
+
- [Example](#example)
|
|
24
|
+
- [Attribs and check function](#attribs-and-check-function)
|
|
9
25
|
|
|
10
|
-
**update**
|
|
11
|
-
* querySelector for attributes fixed
|
|
12
26
|
|
|
27
|
+
## Selector
|
|
13
28
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
29
|
+
Selector is a class which uses HtmlParser for parsing html and Query for parsing html/css query.
|
|
30
|
+
Everything does Query class support, can be selected (so far available all, except pseudos).
|
|
31
|
+
Selector can be used on frontend and on backend.
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
### Basics
|
|
35
|
+
|
|
36
|
+
**Syntax**
|
|
17
37
|
```javascript
|
|
18
|
-
|
|
19
|
-
|
|
38
|
+
let doc = new HtmlSelector(htmlString:string):instanceof HtmlSelector
|
|
39
|
+
doc.$(query:string):object | null
|
|
40
|
+
doc.$$(query:string):array
|
|
41
|
+
doc.$(query:string).$(query:qeury).$$(query:qeury)
|
|
20
42
|
```
|
|
21
43
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
44
|
+
``query`` - html/css query for selecting
|
|
45
|
+
``$`` - method return first element or null
|
|
46
|
+
``$$`` - return collection or empty array
|
|
47
|
+
Each element, has ``$`` and ``$$`` methods for selecting descendants
|
|
48
|
+
|
|
25
49
|
|
|
26
|
-
Example
|
|
50
|
+
**Example**
|
|
27
51
|
```javascript
|
|
28
|
-
let
|
|
29
|
-
let
|
|
52
|
+
let doc = new HtmlSelector(htmlString)
|
|
53
|
+
let body = doc.$('body') // querySelector('body'):element|null
|
|
54
|
+
let divs = body.$$('div') // body.querySelectorAll('div'):array
|
|
55
|
+
console.log(divs)
|
|
30
56
|
```
|
|
31
57
|
|
|
32
58
|
|
|
33
|
-
|
|
59
|
+
#### FrontEnd usage
|
|
60
|
+
```html
|
|
61
|
+
<script src="/node_modules/als-document/document.js"></script>
|
|
62
|
+
<script>
|
|
63
|
+
let htmlText = `
|
|
64
|
+
<div>
|
|
65
|
+
<span>Another one</span>
|
|
66
|
+
<div>Some text</div>
|
|
67
|
+
</div>
|
|
68
|
+
`
|
|
69
|
+
let doc = new HtmlSelector(htmlText)
|
|
70
|
+
let span = doc.$('div>span')
|
|
71
|
+
</script>
|
|
72
|
+
```
|
|
34
73
|
|
|
35
|
-
Document constructor get single string parameter - the outerHTML for converting to virtual DOM tree.
|
|
36
74
|
|
|
75
|
+
#### BackEnd usage
|
|
37
76
|
```javascript
|
|
38
|
-
let
|
|
39
|
-
|
|
77
|
+
let {HtmlSelector} = require('als-document')
|
|
78
|
+
let htmlText = `
|
|
79
|
+
<div>
|
|
80
|
+
<span>Another one</span>
|
|
81
|
+
<div>Some text</div>
|
|
82
|
+
</div>
|
|
83
|
+
`
|
|
84
|
+
let doc = new HtmlSelector(htmlText)
|
|
85
|
+
let span = doc.$('div>span')
|
|
40
86
|
```
|
|
41
87
|
|
|
42
88
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
**Selecting element**
|
|
89
|
+
### Extra abilities
|
|
90
|
+
Usualy, browser selector can select by tag name,class, attribute or id.
|
|
91
|
+
HtmlSelector, has extra selecting options, since id,style,class,events and innerHTML - can be selected as attribute.
|
|
48
92
|
|
|
93
|
+
For example you can do those things:
|
|
49
94
|
```javascript
|
|
50
|
-
|
|
51
|
-
|
|
95
|
+
doc.$('[style*="display"]') //style includes "display"
|
|
96
|
+
doc.$('[onclick*="console.log"]') //event includes "console"
|
|
97
|
+
doc.$('[class*="btn"][class*="danger"]') //class includes "btn" and "danger"
|
|
98
|
+
doc.$('[id^="tab"]') //id starts with "tab-"
|
|
99
|
+
doc.$('[inner$="00"]') //innerText ends with ".00"
|
|
52
100
|
```
|
|
101
|
+
## HtmlParser
|
|
53
102
|
|
|
54
|
-
|
|
55
|
-
* Selects all elements - ``*``
|
|
56
|
-
* element - ``div``
|
|
57
|
-
* class - ``.some-class``
|
|
58
|
-
* id - ``#some-id``
|
|
59
|
-
* parent - ``div > p``
|
|
60
|
-
* next - ``div + p``
|
|
61
|
-
* previous - ``p ~ ul``
|
|
62
|
-
* attribute - ``[some-attribute="some value"]``
|
|
63
|
-
* ``[prop]``
|
|
64
|
-
* ``[prop~=value]``
|
|
65
|
-
* ``[prop|=value]``
|
|
66
|
-
* ``[prop^="value"]``
|
|
67
|
-
* ``[prop$="value"]``
|
|
68
|
-
* ``[prop*="value"]``
|
|
103
|
+
HtmlParser is a class which build dom tree from html string.
|
|
69
104
|
|
|
105
|
+
* HtmlParser removes all html comments and they not included in dom tree.
|
|
106
|
+
* Contrary to regular dom, attribute includes class,id and style as attribute in addition to classList, id and style(as array).
|
|
70
107
|
|
|
71
|
-
The folowing, **won't work**: ``div p``.
|
|
72
108
|
|
|
109
|
+
### Syntax
|
|
73
110
|
|
|
74
|
-
Each returned element, has the folowing:
|
|
75
111
|
```javascript
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
children, // array of childNodes(elements and text nodes) - includes text element too
|
|
82
|
-
tagName, // tag name of element
|
|
83
|
-
id, // id of element if exists (not included in attributes)
|
|
84
|
-
attributes, // object of attributes (id not included)
|
|
85
|
-
classList, // array of classes and add and remove methods
|
|
86
|
-
$(selector),
|
|
87
|
-
$$(selector),
|
|
88
|
-
json(), // remove all methods and circular objects from object
|
|
89
|
-
remove(), // remove this element
|
|
90
|
-
add(element/outerHtml,place),
|
|
91
|
-
add0(element/outerHtml),
|
|
92
|
-
add1(element/outerHtml),
|
|
93
|
-
add2(element/outerHtml),
|
|
94
|
-
add3(element/outerHtml),
|
|
95
|
-
}
|
|
112
|
+
let parsed = new HtmlParser(htmlString:string):instanceof HtmlParser
|
|
113
|
+
parsed.root : circular object
|
|
114
|
+
|
|
115
|
+
// static method
|
|
116
|
+
HtmlParser.parse(html):object // parsed.root
|
|
96
117
|
```
|
|
97
118
|
|
|
98
|
-
|
|
119
|
+
Each element, except root and text elements has:
|
|
120
|
+
* attribs - element's attributes
|
|
121
|
+
* parent - parent element
|
|
122
|
+
* next - next element or null
|
|
123
|
+
* prev - previous element or null
|
|
124
|
+
* children - array of children include text nodes
|
|
125
|
+
* type - tag or text or root
|
|
126
|
+
* classList - array with classes
|
|
127
|
+
* index - start index of element inside elements list
|
|
128
|
+
* id - element's id or null
|
|
129
|
+
* endIndex - end index of element inside elements list
|
|
130
|
+
* level - level in dom tree
|
|
131
|
+
* text - parsed text for tag and for text element
|
|
132
|
+
* innerText:getter - concats all children's text together | ''
|
|
133
|
+
* innerHTML:getter - return innerHTML for element
|
|
134
|
+
* outerHTML:getter - return outerHTML for element
|
|
135
|
+
* ancestors:getter - return array of ancestors
|
|
136
|
+
* getAttribute(name) - return value of attribute or null
|
|
137
|
+
* style:[] - array of styles with camelCase property name
|
|
138
|
+
|
|
139
|
+
Example for parsed.document
|
|
140
|
+
|
|
99
141
|
```javascript
|
|
100
142
|
{
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
143
|
+
type:'root',
|
|
144
|
+
children:[
|
|
145
|
+
{
|
|
146
|
+
attribs: {},
|
|
147
|
+
index: 0,
|
|
148
|
+
prev:null,
|
|
149
|
+
next:{...}
|
|
150
|
+
tag: "!DOCTYPE html",
|
|
151
|
+
type: "tag",
|
|
152
|
+
...
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
attribs: {lang:'en'},
|
|
156
|
+
prev:{...},
|
|
157
|
+
next:null,
|
|
158
|
+
classList:[],
|
|
159
|
+
children:[
|
|
160
|
+
{
|
|
161
|
+
attribs: {},
|
|
162
|
+
children:[...]
|
|
163
|
+
prev:null,
|
|
164
|
+
next:{...}
|
|
165
|
+
classList:[],
|
|
166
|
+
index: 2,
|
|
167
|
+
tag: "head",
|
|
168
|
+
parent:{tag:'html',...} // reference to parent
|
|
169
|
+
type: "tag",
|
|
170
|
+
...
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
attribs: {},
|
|
174
|
+
children:[...]
|
|
175
|
+
index: 10,
|
|
176
|
+
prev:{...},
|
|
177
|
+
next:null,
|
|
178
|
+
classList:[],
|
|
179
|
+
tag: "body",
|
|
180
|
+
parent:{tag:'html',...} // reference to parent
|
|
181
|
+
type: "tag",
|
|
182
|
+
...
|
|
183
|
+
},
|
|
184
|
+
]
|
|
185
|
+
index: 1,
|
|
186
|
+
tag: "html",
|
|
187
|
+
parent:{type:'root',...} // reference to parent
|
|
188
|
+
type: "tag",
|
|
189
|
+
...
|
|
190
|
+
}
|
|
191
|
+
]
|
|
104
192
|
}
|
|
105
193
|
```
|
|
106
194
|
|
|
107
|
-
Comment node:
|
|
108
|
-
```javascript
|
|
109
|
-
tagName:comment,
|
|
110
|
-
comment // comment it self
|
|
111
|
-
```
|
|
112
195
|
|
|
113
|
-
|
|
114
|
-
Example:
|
|
115
|
-
```javascript
|
|
116
|
-
let element = document.$('div')
|
|
117
|
-
element.classList.remove('some')
|
|
118
|
-
element.classList.add('another')
|
|
119
|
-
element.classList.add('onemore')
|
|
120
|
-
```
|
|
196
|
+
### Frontend example
|
|
121
197
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
198
|
+
```html
|
|
199
|
+
<script src="/node_modules/als-document/parser/parser.js"></script>
|
|
200
|
+
<script>
|
|
201
|
+
let result = new HtmlParser(htmlString)
|
|
202
|
+
console.log(result.root)
|
|
127
203
|
|
|
128
|
-
|
|
204
|
+
// Or with static method
|
|
205
|
+
console.log(HtmlParser.parse(html))
|
|
129
206
|
|
|
130
|
-
|
|
131
|
-
json() // remove all methods and circular objects from object
|
|
132
|
-
remove() // remove this element
|
|
133
|
-
add(element/outerHtml,place) // adding AdjacentHTML or AdjacentElement to place(0-3)
|
|
134
|
-
add0(element/outerHtml) // adding AdjacentHTML or AdjacentElement beforebegin
|
|
135
|
-
add1(element/outerHtml) // adding AdjacentHTML or AdjacentElement afterbegin
|
|
136
|
-
add2(element/outerHtml) // adding AdjacentHTML or AdjacentElement beforeend
|
|
137
|
-
add3(element/outerHtml) // adding AdjacentHTML or AdjacentElement afterend
|
|
207
|
+
</script>
|
|
138
208
|
```
|
|
139
209
|
|
|
140
|
-
|
|
210
|
+
### Backend example
|
|
211
|
+
|
|
141
212
|
```javascript
|
|
142
|
-
|
|
143
|
-
let
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
213
|
+
const {HtmlParser} = require('als-htmlparser')
|
|
214
|
+
let result = new HtmlParser(htmlString)
|
|
215
|
+
console.log(result.root)
|
|
216
|
+
|
|
217
|
+
// Or with static method
|
|
218
|
+
console.log(HtmlParser.parse(html))
|
|
148
219
|
```
|
|
149
220
|
|
|
150
221
|
|
|
151
|
-
|
|
222
|
+
## Query
|
|
223
|
+
Query is a class for parsing conditions inside html query. Query not supporting pseudo selectors so far.
|
|
224
|
+
You can use Query on frontend and on backend.
|
|
152
225
|
|
|
153
|
-
|
|
154
|
-
|
|
226
|
+
Query can be used on fronten and on backend.
|
|
227
|
+
|
|
228
|
+
Frontend:
|
|
229
|
+
```html
|
|
230
|
+
<script src="/node_modules/als-document/query/query.js"></script>
|
|
155
231
|
```
|
|
156
232
|
|
|
233
|
+
Backend:
|
|
234
|
+
```javascript
|
|
235
|
+
let {Query} = require('als-document')
|
|
236
|
+
```
|
|
157
237
|
|
|
158
|
-
|
|
159
|
-
To select few elements, use ``$$(selector)`` method.
|
|
238
|
+
### Syntax
|
|
160
239
|
|
|
161
240
|
```javascript
|
|
162
|
-
|
|
241
|
+
let queryObj = new Query(qeury:string): instanceof Query
|
|
242
|
+
let selectors = queryObj.selectors:string
|
|
243
|
+
// or
|
|
244
|
+
let selectors = Query.get(q1:string):string
|
|
163
245
|
```
|
|
164
246
|
|
|
165
|
-
|
|
247
|
+
``query`` - html/css query
|
|
166
248
|
|
|
167
|
-
``each`` method gets callback function with 3 parameters: element it self, index of the element in collection and collection itself.
|
|
168
249
|
|
|
169
|
-
|
|
250
|
+
### Example
|
|
251
|
+
|
|
170
252
|
```javascript
|
|
171
|
-
let
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
253
|
+
let q1 = 'html>body>div.tabs~.some[type $= "radio and some"]>p+div>.some-id .tab-content~input[disabled] div.some'
|
|
254
|
+
let result = new Query(q1).selectors
|
|
255
|
+
let result1 = Query.get(q1)
|
|
256
|
+
// result and result1 has to be same
|
|
257
|
+
console.log(result)
|
|
176
258
|
```
|
|
177
259
|
|
|
178
|
-
|
|
179
|
-
* ``part`` is a part of element. It can be innerText, id, tagName or any property inside attributes.
|
|
180
|
-
* ``fn`` is a filter function which gets content of part. If return true, content will be included.
|
|
181
|
-
|
|
182
|
-
Example:
|
|
260
|
+
Result:
|
|
183
261
|
```javascript
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
262
|
+
[
|
|
263
|
+
{
|
|
264
|
+
"query": "div.some",
|
|
265
|
+
"tag": "div",
|
|
266
|
+
"classList": [
|
|
267
|
+
"some"
|
|
268
|
+
],
|
|
269
|
+
"ancestors": [
|
|
270
|
+
{
|
|
271
|
+
"query": ".some-id",
|
|
272
|
+
"classList": [
|
|
273
|
+
"some-id"
|
|
274
|
+
],
|
|
275
|
+
"parents": [
|
|
276
|
+
{
|
|
277
|
+
"query": "div",
|
|
278
|
+
"tag": "div"
|
|
279
|
+
}
|
|
280
|
+
],
|
|
281
|
+
"prev": {
|
|
282
|
+
"query": "p",
|
|
283
|
+
"tag": "p",
|
|
284
|
+
"parents": [
|
|
285
|
+
{
|
|
286
|
+
"query": ".some[0]",
|
|
287
|
+
"classList": [
|
|
288
|
+
"some"
|
|
289
|
+
],
|
|
290
|
+
"attribs": [
|
|
291
|
+
{
|
|
292
|
+
check:(f),
|
|
293
|
+
"query": "[type$=\"radio and some\"]",
|
|
294
|
+
"name": "type",
|
|
295
|
+
"value": "radio and some",
|
|
296
|
+
"sign": "$="
|
|
297
|
+
}
|
|
298
|
+
]
|
|
299
|
+
}
|
|
300
|
+
],
|
|
301
|
+
"prevAny": {
|
|
302
|
+
"query": "div.tabs",
|
|
303
|
+
"tag": "div",
|
|
304
|
+
"classList": [
|
|
305
|
+
"tabs"
|
|
306
|
+
],
|
|
307
|
+
"parents": [
|
|
308
|
+
{
|
|
309
|
+
"query": "html",
|
|
310
|
+
"tag": "html"
|
|
311
|
+
},
|
|
312
|
+
{
|
|
313
|
+
"query": "body",
|
|
314
|
+
"tag": "body"
|
|
315
|
+
}
|
|
316
|
+
]
|
|
317
|
+
},
|
|
318
|
+
"group": "html>body>div.tabs~.some[0]>p"
|
|
319
|
+
},
|
|
320
|
+
"group": "html>body>div.tabs~.some[0]>p+div>.some-id"
|
|
321
|
+
},
|
|
322
|
+
{
|
|
323
|
+
"query": "input[1]",
|
|
324
|
+
"tag": "input",
|
|
325
|
+
"attribs": [
|
|
326
|
+
{
|
|
327
|
+
"query": "[disabled]",
|
|
328
|
+
"name": "disabled"
|
|
329
|
+
}
|
|
330
|
+
],
|
|
331
|
+
"prevAny": {
|
|
332
|
+
"query": ".tab-content",
|
|
333
|
+
"classList": [
|
|
334
|
+
"tab-content"
|
|
335
|
+
]
|
|
336
|
+
},
|
|
337
|
+
"group": ".tab-content~input[1]"
|
|
338
|
+
}
|
|
339
|
+
],
|
|
340
|
+
"group": "html>body>div.tabs~.some[type $= \"radio and some\"]>p+div>.some-id .tab-content~input[disabled] div.some"
|
|
341
|
+
}
|
|
342
|
+
]
|
|
187
343
|
```
|
|
188
344
|
|
|
189
|
-
|
|
345
|
+
### Attribs and check function
|
|
346
|
+
if attribute has value, attrib object will contain check function with one parameter for value to check.
|
|
190
347
|
|
|
191
|
-
For building html again, use ``build`` method.
|
|
192
|
-
Example:
|
|
193
348
|
```javascript
|
|
194
|
-
let
|
|
195
|
-
|
|
196
|
-
element.classList.remove('some')
|
|
197
|
-
element.id = 'new-id'
|
|
198
|
-
document.build() // return new html text
|
|
199
|
-
document.build([__dirname,'new-index.html']) // will create a file with new html text
|
|
349
|
+
let s = Query.get('[test^="some"]')[0]
|
|
350
|
+
console.log(s.attribs[0].check('some value test')) // true
|
|
200
351
|
```
|
|
352
|
+
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
## Selector
|
|
2
|
+
|
|
3
|
+
Selector is a class which uses HtmlParser for parsing html and Query for parsing html/css query.
|
|
4
|
+
Everything does Query class support, can be selected (so far available all, except pseudos).
|
|
5
|
+
Selector can be used on frontend and on backend.
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### Basics
|
|
9
|
+
|
|
10
|
+
**Syntax**
|
|
11
|
+
```javascript
|
|
12
|
+
let doc = new HtmlSelector(htmlString:string):instanceof HtmlSelector
|
|
13
|
+
doc.$(query:string):object | null
|
|
14
|
+
doc.$$(query:string):array
|
|
15
|
+
doc.$(query:string).$(query:qeury).$$(query:qeury)
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
``query`` - html/css query for selecting
|
|
19
|
+
``$`` - method return first element or null
|
|
20
|
+
``$$`` - return collection or empty array
|
|
21
|
+
Each element, has ``$`` and ``$$`` methods for selecting descendants
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
**Example**
|
|
25
|
+
```javascript
|
|
26
|
+
let doc = new HtmlSelector(htmlString)
|
|
27
|
+
let body = doc.$('body') // querySelector('body'):element|null
|
|
28
|
+
let divs = body.$$('div') // body.querySelectorAll('div'):array
|
|
29
|
+
console.log(divs)
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
#### FrontEnd usage
|
|
34
|
+
```html
|
|
35
|
+
<script src="/node_modules/als-document/document.js"></script>
|
|
36
|
+
<script>
|
|
37
|
+
let htmlText = `
|
|
38
|
+
<div>
|
|
39
|
+
<span>Another one</span>
|
|
40
|
+
<div>Some text</div>
|
|
41
|
+
</div>
|
|
42
|
+
`
|
|
43
|
+
let doc = new HtmlSelector(htmlText)
|
|
44
|
+
let span = doc.$('div>span')
|
|
45
|
+
</script>
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
#### BackEnd usage
|
|
50
|
+
```javascript
|
|
51
|
+
let {HtmlSelector} = require('als-document')
|
|
52
|
+
let htmlText = `
|
|
53
|
+
<div>
|
|
54
|
+
<span>Another one</span>
|
|
55
|
+
<div>Some text</div>
|
|
56
|
+
</div>
|
|
57
|
+
`
|
|
58
|
+
let doc = new HtmlSelector(htmlText)
|
|
59
|
+
let span = doc.$('div>span')
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
### Extra abilities
|
|
64
|
+
Usualy, browser selector can select by tag name,class, attribute or id.
|
|
65
|
+
HtmlSelector, has extra selecting options, since id,style,class,events and innerHTML - can be selected as attribute.
|
|
66
|
+
|
|
67
|
+
For example you can do those things:
|
|
68
|
+
```javascript
|
|
69
|
+
doc.$('[style*="display"]') //style includes "display"
|
|
70
|
+
doc.$('[onclick*="console.log"]') //event includes "console"
|
|
71
|
+
doc.$('[class*="btn"][class*="danger"]') //class includes "btn" and "danger"
|
|
72
|
+
doc.$('[id^="tab"]') //id starts with "tab-"
|
|
73
|
+
doc.$('[inner$="00"]') //innerText ends with ".00"
|
|
74
|
+
```
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
class HtmlSelector {
|
|
2
|
+
constructor(html) {
|
|
3
|
+
if(typeof html == 'string') {
|
|
4
|
+
html = new HtmlParser(html)
|
|
5
|
+
this.html = html
|
|
6
|
+
this.elements = html.elements
|
|
7
|
+
this.makeSelectable()
|
|
8
|
+
} else console.log('Parameter is not string')
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
makeSelectable() {
|
|
12
|
+
this.elements.forEach(element => {
|
|
13
|
+
if(element.type == 'tag' && element.status !== 'close') {
|
|
14
|
+
element.$$ = (query) => this.$$(query,element.index,element.endIndex)
|
|
15
|
+
element.$ = (query) => this.$(query,element.index,element.endIndex)
|
|
16
|
+
}
|
|
17
|
+
})
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
$(query,start=0,end=this.elements.length) {
|
|
21
|
+
return this.$$(query,start,end,true)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
$$(query,start=0,end=this.elements.length,single=false) {
|
|
25
|
+
let result = []
|
|
26
|
+
this.selectors = new Query(query).selectors
|
|
27
|
+
this.query = query
|
|
28
|
+
this.selectors.forEach(selector => {
|
|
29
|
+
for(let i=start; i<end; i++) {
|
|
30
|
+
let el = this.elements[i]
|
|
31
|
+
if(this.checkElement(el,selector) && !result.includes(el)) result.push(el)
|
|
32
|
+
if(single && result.length == 1) break
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
if(single && result.length == 1) return result[0]
|
|
36
|
+
else if(single && result.length == 0) return null
|
|
37
|
+
else return result
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
checkElement(el,selector) {
|
|
41
|
+
if(selector == undefined) return true
|
|
42
|
+
if(el == null) return false
|
|
43
|
+
let {tag,classList,attribs,id,prev,ancestors,parents,prevAny} = selector
|
|
44
|
+
|
|
45
|
+
if(el.status == 'close' || el.type == 'text') return false
|
|
46
|
+
if(id !== undefined && el.id == undefined) return false
|
|
47
|
+
if(id && id !== el.id) return false
|
|
48
|
+
if(tag && el.tag == undefined) return false
|
|
49
|
+
else if(tag && tag !== el.tag) return false
|
|
50
|
+
if(classList !== undefined && el.classList == undefined) return false
|
|
51
|
+
else if(classList !== undefined && Array.isArray(el.classList)) {
|
|
52
|
+
if(classList.every(e => el.classList.includes(e)) == false) return false
|
|
53
|
+
}
|
|
54
|
+
if(this.checkAttribs(attribs,el) == false) return false
|
|
55
|
+
if(this.checkElement(el.prev,prev) == false) return false
|
|
56
|
+
if(this.checkAncestors(el.ancestors,ancestors) == false) return false
|
|
57
|
+
if(this.checkParents(el.ancestors,parents) == false) return false
|
|
58
|
+
if(el.parent) {
|
|
59
|
+
if(this.prevAny(el.parent.children,el.childIndex,prevAny) == false) return false
|
|
60
|
+
}
|
|
61
|
+
return true
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
prevAny(children=[],index,prevAny) {
|
|
65
|
+
let size = children.length
|
|
66
|
+
if((size == 0 || index == 0) && prevAny) return false
|
|
67
|
+
for(let i=index; i>=0; i--) {
|
|
68
|
+
if(this.checkElement(children[i],prevAny)) return true
|
|
69
|
+
}
|
|
70
|
+
return false
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
checkAncestors(ancestors=[],selectorAncestors=[]) {
|
|
74
|
+
let count = 0
|
|
75
|
+
if(selectorAncestors.length == 0) return true
|
|
76
|
+
let endIndex = ancestors.length-1
|
|
77
|
+
let selectorIndex = selectorAncestors.length-1
|
|
78
|
+
while(selectorIndex>=0) {
|
|
79
|
+
for(let i=endIndex; i>=0; i--) {
|
|
80
|
+
endIndex=i-1
|
|
81
|
+
if(this.checkElement(ancestors[i],selectorAncestors[selectorIndex]) == true) {
|
|
82
|
+
count++
|
|
83
|
+
break
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
selectorIndex--
|
|
87
|
+
}
|
|
88
|
+
if(count == selectorAncestors.length) return true
|
|
89
|
+
else return false
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
checkParents(ancestors=[],selectorParents=[]) {
|
|
93
|
+
if(selectorParents.length == 0) return true
|
|
94
|
+
if(ancestors.length < selectorParents.length) return false
|
|
95
|
+
let index = ancestors.length-1
|
|
96
|
+
for(let i=selectorParents.length-1; i>=0; i--) {
|
|
97
|
+
if(this.checkElement(ancestors[index],selectorParents[i]) == false) return false
|
|
98
|
+
index--
|
|
99
|
+
}
|
|
100
|
+
return true
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
checkAttribs(attribs=[],el) {
|
|
104
|
+
let elAttribs = el.attribs
|
|
105
|
+
let names = Object.keys(elAttribs)
|
|
106
|
+
let passedTests = 0
|
|
107
|
+
if(attribs) for(let i=0; i<attribs.length; i++) {
|
|
108
|
+
let {name,value,check} = attribs[i]
|
|
109
|
+
if(name == 'inner' && value !== undefined && check && el.innerText) {
|
|
110
|
+
if(check(el.innerText)) passedTests++
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if(!names.includes(name)) continue
|
|
114
|
+
else if(value == undefined) passedTests++
|
|
115
|
+
else if(value && elAttribs[name]) {
|
|
116
|
+
if(check(elAttribs[name]) == false) continue
|
|
117
|
+
else passedTests++
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
if(passedTests == attribs.length) return true
|
|
121
|
+
else return false
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
try {module.exports = HtmlSelector} catch{}
|