HTML Preparation code:
x
 
1
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
2
3
<script>
4
    (function(exposeAs){
5
        var Dex = function(selector, nodes){
6
7
            // Return Dex object
8
            return new Dex.fn.init(selector, nodes);
9
10
        };
11
12
        Dex.fn = Dex.prototype = {
13
            init: function(selector, nodes){
14
                this.selector = selector;
15
16
                if(nodes){
17
                    this.nodes = nodes;
18
                } else {
19
                    this.nodes = document.querySelectorAll(selector);
20
                }
21
22
                return this;
23
            }
24
25
26
        };
27
28
        // CONSTANTS
29
        var UNDEFINED = "undefined";
30
31
        // HIDDEN VARIABLES
32
        var cachedSelections = {},
33
            delegatedEventListeners = {},
34
            mutationObservers = {},
35
            DOMMutationCallback = function(mutations, selector, parentNodes){
36
                var n, x, y,
37
                    mutationRecord,
38
                    addedNode,
39
                    removedNode,
40
                    modifiedNodeAttribute,
41
                    callbacks,
42
                    modifiedSelector,
43
                    _target,
44
                    executeCallbacks = function(queue, node, arg1, arg2){
45
                        var n = 0;
46
47
                        while(queue[n]){
48
                            // Call callback function
49
                            queue[n].callback.call(node, arg1, arg2);
50
51
                            if(!queue[n].persistant){
52
                                queue.splice(n, 1);
53
                            }
54
55
                            n++;
56
57
                        }
58
                    },
59
                    executeAttributecallbacks = function(queue, node, attrKey, attrValue){
60
                        y = 0;
61
62
                        while(queue[y]){
63
                            // Call callback function
64
                            if(queue[y].attrKey == attrKey){
65
                                queue[y].callback.call(node, attrKey, attrValue);
66
67
                                if(!queue[y].persistant){
68
                                    queue.splice(y, 1);
69
70
                                }
71
72
                            }
73
74
                            y++;
75
                        }
76
                    };
77
78
79
                // Loop through mutation records
80
                for(n = 0; n < mutations.length; n++){
81
                    mutationRecord = mutations[n];
82
83
                    // Loop through added nodes
84
                    if(mutationRecord.addedNodes.length > 0){
85
                        callbacks = mutationObservers[selector].callbacks.onOpen;
86
87
                        if(callbacks){
88
                            for(x = 0; x < mutationRecord.addedNodes.length; x++){
89
                                addedNode = mutationRecord.addedNodes[x];
90
91
                                // Check if node type is valid
92
                                if(addedNode.nodeType == 1){
93
94
                                    // Loop through callbacks
95
                                    for(_target in callbacks){
96
                                        modifiedSelector = callbacks[_target].matchType.modifiedSelector;
97
98
                                        // See if addedNode matches the _target of the callback
99
                                        switch(callbacks[_target].matchType.type){
100
                                            case "tag":
101
                                                if(addedNode.tagName.toUpperCase() == modifiedSelector){
102
                                                    // Loop through all callbacks
103
                                                    executeCallbacks(callbacks[_target].queue, addedNode);
104
105
                                                }
106
                                                break;
107
                                            case "id":
108
                                                if(addedNode.id == modifiedSelector){
109
                                                    // Loop through all callbacks
110
                                                    executeCallbacks(callbacks[_target].queue, addedNode);
111
112
                                                }
113
                                                break;
114
                                            case "classname":
115
                                                if(addedNode.classList.contains(modifiedSelector)){
116
                                                    // Loop through all callbacks
117
                                                    executeCallbacks(callbacks[_target].queue, addedNode, mutationRecord.addedNodes);
118
119
                                                }
120
                                                break;
121
                                            case "complex":
122
                                                if(addedNode.matches(modifiedSelector)){
123
                                                    // Loop through all callbacks
124
                                                    executeCallbacks(callbacks[_target].queue, addedNode);
125
126
                                                }
127
                                                break;
128
129
                                        }
130
131
                                    }
132
                                }
133
134
                            }
135
                        }
136
                    }
137
138
                    if(mutationRecord.removedNodes.length > 0){
139
                        callbacks = mutationObservers[selector].callbacks.onClose;
140
141
                        if(callbacks){
142
                            // Loop through removed nodes
143
                            for(x = 0; x < mutationRecord.removedNodes.length; x++){
144
                                removedNode = mutationRecord.removedNodes[x];
145
146
                                // Check if node type is valid
147
                                if(removedNode.nodeType == 1){
148
149
                                    // Loop through callbacks
150
                                    for(_target in callbacks){
151
152
                                        modifiedSelector = callbacks[_target].matchType.modifiedSelector;
153
154
                                        // See if removedNode matches the _target of the callback
155
                                        switch(callbacks[_target].matchType.type){
156
                                            case "tag":
157
                                                if(removedNode.tagName.toUpperCase() == modifiedSelector){
158
                                                    // Loop through all callbacks
159
                                                    executeCallbacks(callbacks[_target].queue, removedNode);
160
161
                                                }
162
                                                break;
163
                                            case "id":
164
                                                if(removedNode.id == modifiedSelector){
165
                                                    // Loop through all callbacks
166
                                                    executeCallbacks(callbacks[_target].queue, removedNode);
167
168
                                                }
169
                                                break;
170
                                            case "classname":
171
                                                if(removedNode.classList.contains(modifiedSelector)){
172
                                                    // Loop through all callbacks
173
                                                    executeCallbacks(callbacks[_target].queue, removedNode);
174
175
                                                }
176
                                                break;
177
                                            case "complex":
178
                                                if(removedNode.matches(modifiedSelector)){
179
                                                    // Loop through all callbacks
180
                                                    executeCallbacks(callbacks[_target].queue, removedNode);
181
182
                                                }
183
                                                break;
184
185
                                        }
186
187
                                    }
188
189
                                }
190
191
                            }
192
                        }
193
                    }
194
195
                    if(mutationRecord.attributeName){
196
                        callbacks = mutationObservers[selector].callbacks.onAttribute;
197
198
                        if(callbacks){
199
                            // Loop through modified attributes
200
                            modifiedNodeAttribute = mutationRecord.target;
201
202
                            // Loop through callbacks
203
                            for(_target in callbacks){
204
                                modifiedSelector = callbacks[_target].matchType.modifiedSelector;
205
206
                                // See if modifiedNodeAttribute matches the _target of the callback
207
                                switch(callbacks[_target].matchType.type){
208
                                    case "tag":
209
                                        if(modifiedNodeAttribute.tagName.toUpperCase() == modifiedSelector){
210
                                            // Loop through all callbacks
211
                                            if(mutationRecord.target.attributes[mutationRecord.attributeName]){ // Its a live list, so check it hasnt been removed
212
                                                executeAttributecallbacks(callbacks[_target].queue, modifiedNodeAttribute, mutationRecord.attributeName, mutationRecord.target.attributes[mutationRecord.attributeName].value);
213
214
                                            }
215
216
                                        }
217
                                        break;
218
                                    case "id":
219
                                        if(modifiedNodeAttribute.id == modifiedSelector){
220
                                            // Loop through all callbacks
221
                                            if(mutationRecord.target.attributes[mutationRecord.attributeName]){ // Its a live list, so check it hasnt been removed
222
                                                executeAttributecallbacks(callbacks[_target].queue, modifiedNodeAttribute, mutationRecord.attributeName, mutationRecord.target.attributes[mutationRecord.attributeName].value);
223
224
                                            }
225
                                        }
226
                                        break;
227
                                    case "classname":
228
                                        if(modifiedNodeAttribute.classList.contains(modifiedSelector)){
229
                                            // Loop through all callbacks
230
                                            if(mutationRecord.target.attributes[mutationRecord.attributeName]){ // Its a live list, so check it hasnt been removed
231
                                                executeAttributecallbacks(callbacks[_target].queue, modifiedNodeAttribute, mutationRecord.attributeName, mutationRecord.target.attributes[mutationRecord.attributeName].value);
232
233
                                            }
234
                                        }
235
                                        break;
236
                                    case "complex":
237
                                        if(modifiedNodeAttribute.matches(modifiedSelector)){
238
                                            // Loop through all callbacks
239
                                            if(mutationRecord.target.attributes[mutationRecord.attributeName]){ // Its a live list, so check it hasnt been removed
240
                                                executeAttributecallbacks(callbacks[_target].queue, modifiedNodeAttribute, mutationRecord.attributeName, mutationRecord.target.attributes[mutationRecord.attributeName].value);
241
242
                                            }
243
                                        }
244
                                        break;
245
                                }
246
247
                            }
248
                        }
249
                    }
250
251
                }
252
253
            },
254
            createEventListener = function(dexObject, eventType, selector, target, callback, persistant){
255
                // Check that the event type exists in queue
256
                if(!delegatedEventListeners[eventType]){
257
                    delegatedEventListeners[eventType] = {};
258
                }
259
260
                // Check that this selector is registered
261
                if(!delegatedEventListeners[eventType][selector]){
262
                    delegatedEventListeners[eventType][selector] = {};
263
264
                    // Setup listener
265
266
                    attachEventListeners(eventType, dexObject);
267
                }
268
269
                // Check that this target is registered
270
                if(!delegatedEventListeners[eventType][selector][target]){
271
                    delegatedEventListeners[eventType][selector][target] = [];
272
                }
273
274
                // register delegated event listener
275
                delegatedEventListeners[eventType][selector][target].push({
276
                    callback: callback,
277
                    persistant: persistant
278
                });
279
280
281
            },
282
            attachEventListeners = function(eventType, dexObject){
283
284
                var n,
285
                    selector = {
286
                        string: dexObject.selector
287
                    };
288
289
                for(n = 0; n < dexObject.nodes.length; n++){
290
                    selector.node = dexObject.nodes[n];
291
292
                    selector.node.addEventListener(eventType, function(e){
293
                        var eventHandlers = delegatedEventListeners[eventType][selector.string];
294
295
                        for(handlerSelector in eventHandlers){
296
297
                            clickedNode = e.target;
298
299
300
                            while(  clickedNode &&
301
                                    clickedNode !== document &&
302
                                    clickedNode !== selector.node) { // Stop looking when we hit the node that contains the event listener
303
304
                                if (clickedNode.matches(handlerSelector)) {
305
306
                                    var handlerIndex = 0;
307
308
                                    while (eventHandlers[handlerSelector][handlerIndex]){
309
                                        eventHandlers[handlerSelector][handlerIndex].callback.call(clickedNode, e);
310
311
                                        if(!eventHandlers[handlerSelector][handlerIndex].persistant){
312
                                            eventHandlers[handlerSelector].splice(handlerIndex, 1);
313
                                        }
314
315
                                        handlerIndex++;
316
                                    }
317
318
                                }
319
320
                                clickedNode = clickedNode.parentNode;
321
322
                            }
323
                        }
324
325
                    });
326
                }
327
328
329
            };
330
331
        // EXPOSED SELECTOR FUNCTIONS
332
        Dex.fn.init.prototype = {
333
334
            first: function(){
335
                /* Remove all nodes except first from node list */
336
337
                this.nodes = [this.nodes[0]];
338
339
                return this;
340
            },
341
342
            index: function(index){
343
                /* Remove all nodes except requested index */
344
345
                this.nodes = [this.nodes[index]];
346
347
                return this;
348
            },
349
350
            getNode: function(index){
351
                /* Return DOM node */
352
353
                return this.nodes[index];
354
            },
355
356
            exists: function(){
357
                /* See if the node list contains at least one node */
358
359
                return this.nodes[0] != null;
360
            },
361
362
            appendClone: function(node){
363
364
                var n;
365
366
                for(n = 0; n < this.nodes.length; n++){
367
                    this.nodes[n].appendChild(node.cloneNode(true));
368
                }
369
370
                return this;
371
372
            },
373
374
            append: function(node){
375
376
                var n;
377
378
                for(n = 0; n < this.nodes.length; n++){
379
                    this.nodes[n].appendChild(node);
380
                }
381
382
                return this;
383
384
            },
385
386
            remove: function(){
387
388
                var n;
389
390
                for(n = 0; n < this.nodes.length; n++){
391
                    this.nodes[n];
392
393
                    if (this.nodes[n].parentNode) {
394
                        this.nodes[n].parentNode.removeChild(this.nodes[n]);
395
                    }
396
                }
397
398
                return this;
399
400
            },
401
402
            clone: function(){
403
                var n,
404
                    clonedNodes = [];
405
406
                for(n = 0; n < this.nodes.length; n++){
407
                    clonedNodes.push(this.nodes[n].cloneNode(true));
408
                }
409
410
                this.nodes = clonedNodes;
411
412
                return this;
413
            },
414
415
            parent: function(){
416
417
                this.nodes = [this.nodes[0].parentNode];
418
419
                return this;
420
421
            },
422
423
            closest: function(matchSelector){
424
425
                var closest = [];
426
427
                node = this.nodes[0];
428
429
                // Go through parents
430
                while(node && node !== document) {
431
                    if (node.matches(matchSelector)) {
432
433
                        closest = [node];
434
                        break;
435
436
                    }
437
438
                    node = node.parentNode;
439
440
                }
441
442
                this.nodes = closest;
443
444
                return this;
445
446
            },
447
448
            filter: function(selector){
449
                /* Filter then current node list - note: only filters on the first node in list */
450
451
                this.nodes = this.nodes[0].querySelectorAll(selector);
452
453
                return this;
454
455
            },
456
457
            // DOM modification
458
            setHTML: function(value){
459
                /* Set innerHTML of all nodes in nodelist */
460
461
                var n;
462
463
                for(n = 0; n < this.nodes.length; n++){
464
                    this.nodes[n].innerHTML = value;
465
                }
466
467
                return this;
468
            },
469
470
            getHTML: function(value){
471
                /* Get innerHTML of all first node in nodelist */
472
473
                return this.nodes[0].innerHTML;
474
            },
475
476
            css: function(styles){
477
                /* Set CSS of all nodes in nodelist */
478
479
                var style,
480
                    n;
481
482
                for(n = 0; n < this.nodes.length; n++){
483
484
                    for(style in styles){
485
486
                        this.nodes[n].style[style] = styles[style];
487
488
                    }
489
490
                }
491
492
                return this;
493
494
495
496
            },
497
498
            setAttribute: function(key, value){
499
                /* Set attribute of all nodes in nodelist */
500
501
                var n;
502
503
                for(n = 0; n < this.nodes.length; n++){
504
                    this.nodes[n].setAttribute(key, value);
505
                }
506
507
                return this;
508
            },
509
510
            getAttribute: function(key){
511
                /* Get attribute of first node in nodelist */
512
513
                return this.nodes[0].getAttribute(key);
514
            },
515
516
            toggleAttribute: function(key, value){
517
                /* Get attribute of first node in nodelist */
518
519
                var n,
520
                    attrValue;
521
522
                for(n = 0; n < this.nodes.length; n++){
523
                    attrValue = this.nodes[n].getAttribute(key);
524
525
                    if(attrValue == value[0]){
526
                        this.nodes[n].setAttribute(key, value[1]);
527
                    } else {
528
                        this.nodes[n].setAttribute(key, value[0]);
529
                    }
530
                }
531
532
                return this;
533
            },
534
535
            cache: function(cacheID){
536
                /* Cache node list with cacheID as ID */
537
538
                cachedSelections[cacheID] = this;
539
            },
540
541
            // Classes
542
            addClass: function(classname){
543
                /* Add class to all nodes in nodelist */
544
545
546
                var n;
547
548
                for(n = 0; n < this.nodes.length; n++){
549
                    this.nodes[n].classList.add(classname);
550
                }
551
552
                return this;
553
            },
554
555
            removeClass: function(classname){
556
                /* Remove class from all nodes in nodelist */
557
558
                var n;
559
560
                for(n = 0; n < this.nodes.length; n++){
561
                    this.nodes[n].classList.remove(classname);
562
                }
563
                return this;
564
            },
565
566
            hasClass: function(classname){
567
                /* Check whether first node in list contains a classname */
568
569
                var result = false;
570
571
                if(this.nodes[0]){
572
                    result = this.nodes[0].classList.contains(classname);
573
                }
574
575
                return result;
576
577
            },
578
579
            toggleClass: function(classname){
580
                /* Toggle classnames on all nodes in nodelist */
581
582
                var n;
583
584
                for(n = 0; n < this.nodes.length; n++){
585
                    this.nodes[n].classList.toggle(classname);
586
                }
587
588
                return this;
589
            },
590
591
            replaceClass: function(old_classname, new_classname){
592
                /* Replace old_classname with new_classname on all nodes in nodelist */
593
594
                var n;
595
596
                for(n = 0; n < this.nodes.length; n++){
597
                    this.nodes[n].classList.replace(old_classname, new_classname);
598
                }
599
600
                return this;
601
            },
602
603
            trigger: function(eventType){
604
                /* Trigger eventType (click, mouseover, etc, ...) on all nodes in nodelist */
605
606
                if(this.nodes[0]){
607
                    var clickEvent = document.createEvent("MouseEvents");
608
609
                    clickEvent.initEvent(eventType, true, true);
610
                    this.nodes[0].dispatchEvent(clickEvent);
611
                }
612
613
                return this;
614
            },
615
616
            onOpen: function(target, callback){
617
                this.onMutation("onOpen", target, callback, {
618
                    children: false,
619
                    persistant: true
620
                });
621
622
                return this;
623
            },
624
625
            onceOpen: function(target, callback){
626
                this.onMutation("onOpen", target, callback, {
627
                    children: false,
628
                    persistant: false
629
                });
630
631
                return this;
632
            },
633
634
            onClose: function(target, callback){
635
636
                this.onMutation("onClose", target, callback, {
637
                    children: false,
638
                    persistant: true
639
                });
640
641
                return this;
642
            },
643
644
            onceClose: function(target, callback){
645
646
                this.onMutation("onClose", target, callback, {
647
                    children: false,
648
                    persistant: false
649
                });
650
651
                return this;
652
            },
653
654
            onAttribute: function(target, attrKey, callback){
655
                this.onMutation("onAttribute", target, callback, {
656
                    attrKey: attrKey,
657
                    persistant: true
658
                });
659
660
                return this;
661
            },
662
663
            onceAttribute: function(target, attrKey, callback){
664
                this.onMutation("onAttribute", target, callback, {
665
                    attrKey: attrKey,
666
                    persistant: false
667
                });
668
669
                return this;
670
            },
671
672
            onMutation: function(mutationType, target, callback, parameters){
673
                var selector = this.selector,
674
                    parentNodes = this.nodes,
675
                    matchType = function(target){
676
                        // This needs updating to cater for complex selectors.
677
678
                        var result;
679
680
                        if(target[0] == "."){
681
                            result = {
682
                                type: "classname",
683
                                modifiedSelector: target.slice(1)
684
                            }
685
686
687
                        } else if(target[0] == "#"){
688
                            result = {
689
                                type: "id",
690
                                modifiedSelector: target.slice(1)
691
                            }
692
693
                        } else if(target[0] == "["){
694
                            result = {
695
                                type: "complex",
696
                                modifiedSelector: target
697
                            }
698
699
                        } else {
700
                            result = {
701
                                type: "tag",
702
                                modifiedSelector: target.toUpperCase()
703
                            }
704
                        }
705
706
                        return result;
707
708
                    }(target),
709
                    n,
710
                    parameters = parameters || {};
711
712
713
                // See if we need to attach a mutation observer
714
715
716
                if(!mutationObservers[this.selector]){
717
                    mutationObservers[this.selector] = {
718
                        observer: new MutationObserver(function(mutations){
719
                            DOMMutationCallback(mutations, selector, parentNodes);
720
                        }),
721
                        callbacks: {}
722
                    }
723
724
                    // Attach observer to all matches nodes
725
                    for(n = 0; n < this.nodes.length; n++){
726
                        mutationObservers[this.selector].observer.observe(this.nodes[n], {
727
                            attributes: true,
728
                            childList: true,
729
                            characterData: false,
730
                            subtree: true
731
                        });
732
                    }
733
                }
734
735
736
737
                // See if there are already callbacks for mutationType
738
                if(!mutationObservers[this.selector].callbacks[mutationType]){
739
                    mutationObservers[this.selector].callbacks[mutationType] = {}
740
                }
741
742
                // See if there are already calbacks for this target
743
                if(!mutationObservers[this.selector].callbacks[mutationType][target]){
744
                    // mutationObservers[this.selector][childtree].callbacks[mutationType][target] = []
745
                    mutationObservers[this.selector].callbacks[mutationType][target] = {
746
                        matchType: matchType,
747
                        queue: []
748
                    }
749
                }
750
751
                // Save callback
752
                mutationObservers[this.selector].callbacks[mutationType][target].queue.push({
753
                    callback: callback,
754
                    attrKey: parameters.attrKey,
755
                    persistant: parameters.persistant
756
                });
757
            },
758
759
760
            onClick: function(target, callback){
761
                createEventListener(this, "click", this.selector, target, callback, true);
762
763
                return this;
764
765
            },
766
767
            oneClick: function(target, callback){
768
                createEventListener(this, "click", this.selector, target, callback, false);
769
770
                return this;
771
772
            },
773
774
            onMouseUp: function(target, callback){
775
                createEventListener(this, "mouseup", this.selector, target, callback, true);
776
777
                return this;
778
779
            },
780
781
            oneMouseUp: function(target, callback){
782
                createEventListener(this, "mouseup", this.selector, target, callback, false);
783
784
                return this;
785
786
            },
787
788
            onMouseDown: function(target, callback){
789
                createEventListener(this, "mousedown", this.selector, target, callback, true);
790
791
                return this;
792
793
            },
794
795
            oneMouseDown: function(target, callback){
796
                createEventListener(this, "mousedown", this.selector, target, callback, false);
797
798
                return this;
799
800
            },
801
802
            onMouseEnter: function(target, callback){
803
                createEventListener(this, "mouseenter", this.selector, target, callback, true);
804
805
                return this;
806
807
            },
808
809
            oneMouseEnter: function(target, callback){
810
                createEventListener(this, "mouseenter", this.selector, target, callback, false);
811
812
                return this;
813
814
            },
815
816
            onMouseLeave: function(target, callback){
817
                createEventListener(this, "mouseleave", this.selector, target, callback, true);
818
819
                return this;
820
821
            },
822
823
            oneMouseLeave: function(target, callback){
824
                createEventListener(this, "mouseleave", this.selector, target, callback, false);
825
826
                return this;
827
828
            },
829
830
            onMouseOver: function(target, callback){
831
                createEventListener(this, "mouseover", this.selector, target, callback, true);
832
833
                return this;
834
835
            },
836
837
            oneMouseOver: function(target, callback){
838
                createEventListener(this, "mouseover", this.selector, target, callback, false);
839
840
                return this;
841
842
            },
843
844
            onMouseOut: function(target, callback){
845
                createEventListener(this, "mouseout", this.selector, target, callback, true);
846
847
                return this;
848
849
            },
850
851
            oneMouseOut: function(target, callback){
852
                createEventListener(this, "mouseout", this.selector, target, callback, false);
853
854
                return this;
855
856
            },
857
858
859
        }
860
861
        Dex.appendCSS = function(url){
862
            var head = document.head,
863
                link = document.createElement('link');
864
865
              link.type = "text/css";
866
              link.rel = "stylesheet";
867
              link.href = url;
868
869
              head.appendChild(link);
870
871
              return link;
872
        }
873
874
        Dex.getCached = function(cacheID){
875
            return cachedSelections[cacheID];
876
        }
877
878
        Dex.clearCache = function(cacheID){
879
            delete cachedSelections[cacheID];
880
881
        }
882
883
        Dex.tag = function(tag){
884
            /* Use Tag() to select nodes using the getElementsByTagName */
885
886
            var nodes = document.getElementsByTagName(tag);
887
888
            return Dex(tag, nodes);
889
890
        }
891
892
        Dex.class = function(classname){
893
            /* Use Tag() to select nodes using the getElementsByClassName */
894
895
            var nodeCollection = document.getElementsByClassName(classname),
896
                nodes = Array.prototype.slice.call(nodeCollection);
897
898
            return Dex("." + classname, nodes);
899
900
        }
901
902
        Dex.iframe = function(selector){
903
904
            var iframe = document.querySelectorAll(selector)[0];
905
906
            return Dex.node(iframe.contentDocument || iframe.contentWindow.document);
907
908
        }
909
910
        Dex.id = function(id){
911
            /* Use Tag() to select nodes using the getElementById */
912
913
            var nodes = [document.getElementById(id)];
914
915
            return Dex("#" + id, nodes);
916
917
        }
918
919
        Dex.node = function(node){
920
            /* Use Node to create a Dex object with a DOM node directly */
921
922
            return Dex("node", [node]);
923
924
        }
925
926
        Dex.collection = function(nodeCollection){
927
            /* Use Node to create a Dex object with an HTML Node Collection  directly */
928
929
            var nodes = [];
930
931
            for(n = 0; n < nodeCollection.length; n++){
932
                nodes.push(nodeCollection[n]);
933
            }
934
935
            return Dex("node", nodes)
936
        }
937
938
        Dex.dump = function(object){
939
            console.log(mutationObservers);
940
        };
941
942
        if(exposeAs){
943
            window[exposeAs] = Dex;
944
        }
945
946
    })("DexV2");
947
</script>
948
949
<style>
950
        .add-colour {
951
            background: red;
952
        }
953
</style>
954
955
<section>
956
            <table>
957
                <tbody>
958
                    <tr>
959
                        <td>
960
                            <div class="app-container">
961
                                <table>
962
                                    <tbody>
963
                                        <tr>
964
                                            <td>
965
                                                <div class="app-window">
966
                                                    App window
967
                                                    <table>
968
                                                        <tbody>
969
                                                            <tr>
970
                                                                <td>
971
                                                                    <div class="app-menu">
972
                                                                        app menu
973
                                                                        <div>
974
                                                                            <div>
975
                                                                                <ul>
976
                                                                                    <li>
977
                                                                                        <span class="title">This is a list item in an unordered list</span>
978
                                                                                    </li>
979
                                                                                    <li>
980
                                                                                        <button class="click-me-ul">ul .click-me</button>
981
                                                                                        <button class="and-me">ul .and-me</button>
982
                                                                                    </li>
983
984
                                                                                </ul>
985
986
                                                                                <ol>
987
                                                                                    <li>
988
                                                                                        <span class="title">This is a list item in an ordered list</span>
989
                                                                                    </li>
990
                                                                                    <li>
991
                                                                                        <button class="click-me-ol">ol .click-me</button>
992
                                                                                        <button class="and-me">ol .and-me</button>
993
                                                                                    </li>
994
                                                                                </ol>
995
                                                                            </div>
996
                                                                            <span id="output" class="check-me">This is the output area</span>
997
                                                                        </div>
998
                                                                    </div>
999
                                                                </td>
1000
                                                            </tr>
1001
                                                        </tbody>
1002
                                                    </table>
1003
                                                </div>
1004
                                            </td>
1005
                                        </tr>
1006
                                    </tbody>
1007
                                </table>
1008
                            </div>
1009
                        </td>
1010
                    </tr>
1011
                </tbody>
1012
            </table>
1013
        </section>
Tests:
  • Dex Add Class

     
    DexV2.id("output").addClass("add-colour");
  • Dex Remove Class

     
    DexV2.id("output").removeClass("check-me");
  • Dex Has Class

     
    DexV2.id("output").hasClass("check-me")
  • Dex Toggle Class

     
    DexV2.id("output").toggleClass("check-me")
  • Dex Replace Class

     
    DexV2.id("output").replaceClass("check-me", "updated-class")
  • jQuery Add Class

     
    $("#output").addClass("add-colour");
  • jQuery Remove Class

     
    $("#output").removeClass("check-me");
  • jQuery Has Class

     
    $("#output").hasClass("check-me")
  • jQuery Replace Class

     
    $("#output")
        .removeClass("check-me")
        .addClass("updated-class");
  • jQuery Toggle Class

     
    $("#output").toggleClass("check-me")
Rendered benchmark preparation results:

Suite status: <idle, ready to run>

Previous results

Experimental features:

  • Test case name Result
    Dex Add Class
    Dex Remove Class
    Dex Has Class
    Dex Toggle Class
    Dex Replace Class
    jQuery Add Class
    jQuery Remove Class
    jQuery Has Class
    jQuery Replace Class
    jQuery Toggle Class

    Fastest: N/A

    Slowest: N/A

Latest run results:
Run details: (Test run date: 7 years ago)
Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36
Chrome 61 on Mac OS X 10.12.5
View result in a separate tab
Test name Executions per second
Dex Add Class 1039579.9 Ops/sec
Dex Remove Class 1375333.4 Ops/sec
Dex Has Class 3342226.0 Ops/sec
Dex Toggle Class 925498.1 Ops/sec
Dex Replace Class 2631932.2 Ops/sec
jQuery Add Class 540185.7 Ops/sec
jQuery Remove Class 517865.7 Ops/sec
jQuery Has Class 849267.9 Ops/sec
jQuery Replace Class 335583.7 Ops/sec
jQuery Toggle Class 199303.6 Ops/sec