10
10
11
11
function focusNavigationHeuristics ( spatnavPolyfillOptions ) {
12
12
// condition: focus delegation model = false
13
+ spatnavPolyfillOptions = spatnavPolyfillOptions || { 'standardName' : true } ;
13
14
14
15
const ARROW_KEY_CODE = { 37 : 'left' , 38 : 'up' , 39 : 'right' , 40 : 'down' } ;
15
- const spinnableInputTypes = [ 'email' , 'date' , 'month' , 'number' , 'time' , 'week' ] ;
16
- const textInputTypes = [ 'password' , 'text' , 'search' , 'tel' , 'url' ] ;
17
-
18
- if ( ! spatnavPolyfillOptions )
19
- spatnavPolyfillOptions = { "standardName" : "true" } ;
20
16
21
17
// Load SpatNav API lib
22
18
const SpatNavAPI = SpatnavAPI ( ) ;
@@ -31,16 +27,13 @@ function focusNavigationHeuristics(spatnavPolyfillOptions) {
31
27
document . addEventListener ( 'keydown' , function ( e ) {
32
28
let focusNavigableArrowKey = { 'left' : true , 'up' : true , 'right' : true , 'down' : true } ;
33
29
const eventTarget = document . activeElement ;
30
+ const dir = ARROW_KEY_CODE [ e . keyCode ] ;
34
31
35
- let dir = ARROW_KEY_CODE [ e . keyCode ] ;
36
32
// Edge case (text input, area) : Don't move focus, just navigate cursor in text area
37
33
if ( ( eventTarget . nodeName === 'INPUT' ) || eventTarget . nodeName === 'TEXTAREA' )
38
34
focusNavigableArrowKey = handlingEditableElement ( e ) ;
39
35
40
- if ( ! focusNavigableArrowKey [ dir ] ) {
41
- dir = null ;
42
- }
43
- if ( dir ) {
36
+ if ( focusNavigableArrowKey [ dir ] ) {
44
37
e . preventDefault ( ) ;
45
38
navigate ( dir ) ;
46
39
}
@@ -173,8 +166,7 @@ function focusNavigationHeuristics(spatnavPolyfillOptions) {
173
166
* @returns NaN
174
167
*/
175
168
function focusingController ( bestCandidate , dir ) {
176
- const eventTarget = document . activeElement ;
177
- const container = eventTarget . getSpatnavContainer ( ) ;
169
+ const container = document . activeElement . getSpatnavContainer ( ) ;
178
170
179
171
// When bestCandidate is found
180
172
if ( bestCandidate ) {
@@ -282,7 +274,6 @@ function focusNavigationHeuristics(spatnavPolyfillOptions) {
282
274
*/
283
275
function filteredCandidates ( currentElm , candidates , dir , container ) {
284
276
const originalContainer = currentElm . getSpatnavContainer ( ) ;
285
- let filteredcandidates = [ ] ;
286
277
let eventTargetRect ;
287
278
288
279
// to do
@@ -293,22 +284,17 @@ function focusNavigationHeuristics(spatnavPolyfillOptions) {
293
284
294
285
// If D(dir) is null, let candidates be the same as visibles
295
286
if ( dir === undefined )
296
- filteredcandidates = candidates ;
287
+ return candidates ;
297
288
298
289
/*
299
290
* Else, let candidates be the subset of the elements in visibles
300
291
* whose principal box’s geometric center is within the closed half plane
301
292
* whose boundary goes through the geometric center of starting point and is perpendicular to D.
302
293
*/
303
- else
304
- for ( let i = 0 ; i < candidates . length ; i ++ ) {
305
- const candidateContainer = candidates [ i ] . getSpatnavContainer ( ) ;
306
- const candidateRect = candidates [ i ] . getBoundingClientRect ( ) ;
307
- if ( container . contains ( candidateContainer ) && isOutside ( candidateRect , eventTargetRect , dir ) )
308
- filteredcandidates . push ( candidates [ i ] ) ;
309
- }
310
-
311
- return filteredcandidates ;
294
+ return candidates . filter ( candidate =>
295
+ container . contains ( candidate . getSpatnavContainer ( ) ) &&
296
+ isOutside ( candidate . getBoundingClientRect ( ) , eventTargetRect , dir )
297
+ ) ;
312
298
}
313
299
314
300
/*
@@ -442,16 +428,7 @@ function focusNavigationHeuristics(spatnavPolyfillOptions) {
442
428
* @returns {sequence<Node> } - visible focusable areas
443
429
*/
444
430
function findVisibles ( focusables ) {
445
- const visibles = [ ] ;
446
- let thisElement = undefined ;
447
-
448
- for ( let i = 0 ; i < focusables . length ; i ++ ) {
449
- thisElement = focusables [ i ] ;
450
- if ( isVisible ( thisElement ) ) {
451
- visibles . push ( thisElement ) ;
452
- }
453
- }
454
- return visibles ;
431
+ return focusables . filter ( isVisible ) ;
455
432
}
456
433
457
434
/**
@@ -463,7 +440,7 @@ function focusNavigationHeuristics(spatnavPolyfillOptions) {
463
440
function findStartingPoint ( ) {
464
441
let startingPoint = document . activeElement ;
465
442
if ( ! startingPoint ||
466
- ( startingPoint == document . body && ! document . querySelector ( ':focus' ) ) /* body isn't actually focused*/
443
+ ( startingPoint === document . body && ! document . querySelector ( ':focus' ) ) /* body isn't actually focused*/
467
444
) {
468
445
startingPoint = document ;
469
446
}
@@ -518,85 +495,54 @@ function focusNavigationHeuristics(spatnavPolyfillOptions) {
518
495
}
519
496
520
497
/* Whether this element is scrollable or not */
521
- function isScrollable ( ) { // element, dir
522
- // parameter: element
523
- if ( ( arguments . length == 1 && typeof arguments [ 0 ] === 'object' ) ||
524
- ( arguments . length == 2 && typeof arguments [ 0 ] === 'object' && arguments [ 1 ] == null ) ) {
525
- const element = arguments [ 0 ] ;
526
-
527
- if ( element . nodeName === 'HTML' || element . nodeName === 'BODY' ) return true ;
528
- else if ( isScrollContainer ( element ) && isOverflow ( element ) ) return true ;
529
- else return false ;
498
+ function isScrollable ( element , dir ) { // element, dir
499
+ if ( element && typeof element === 'object' ) {
500
+ if ( dir && typeof dir === 'string' ) { // parameter: dir, element
501
+ if ( isOverflow ( element , dir ) ) {
502
+ // style property
503
+ const overflowX = window . getComputedStyle ( element , null ) . getPropertyValue ( 'overflow-x' ) ;
504
+ const overflowY = window . getComputedStyle ( element , null ) . getPropertyValue ( 'overflow-y' ) ;
505
+
506
+ switch ( dir ) {
507
+ case 'left' :
508
+ /* falls through */
509
+ case 'right' :
510
+ return ( overflowX !== 'visible' && overflowX !== 'clip' ) ;
511
+ case 'up' :
512
+ /* falls through */
513
+ case 'down' :
514
+ return ( overflowY !== 'visible' && overflowY !== 'clip' ) ;
515
+ }
516
+ }
517
+ return false ;
518
+ } else { // parameter: element
519
+ return ( element . nodeName === 'HTML' || element . nodeName === 'BODY' ) ||
520
+ ( isScrollContainer ( element ) && isOverflow ( element ) ) ;
521
+ }
530
522
}
523
+ console . log ( 'Need parameters for isScrollable()' ) ;
524
+ return false ;
525
+ }
531
526
532
- // parameter: dir, element
533
- else if ( arguments . length == 2 && typeof arguments [ 0 ] === 'object'
534
- && typeof arguments [ 1 ] === 'string' ) {
535
- const element = arguments [ 0 ] ;
536
- const dir = arguments [ 1 ] ;
537
-
538
- if ( isOverflow ( element , dir ) ) {
539
- // style property
540
- const overflowX = window . getComputedStyle ( element , null ) . getPropertyValue ( 'overflow-x' ) ;
541
- const overflowY = window . getComputedStyle ( element , null ) . getPropertyValue ( 'overflow-y' ) ;
542
-
527
+ /* Whether this element is overflow or not */
528
+ function isOverflow ( element , dir ) {
529
+ if ( element && typeof element === 'object' ) {
530
+ if ( dir && typeof dir === 'string' ) { // parameter: element, dir
543
531
switch ( dir ) {
544
532
case 'left' :
545
533
/* falls through */
546
534
case 'right' :
547
- return ( overflowX !== 'visible' && overflowX !== 'clip' ) ;
535
+ return ( element . scrollWidth > element . clientWidth ) ;
548
536
case 'up' :
549
537
/* falls through */
550
538
case 'down' :
551
- return ( overflowY !== 'visible' && overflowY !== 'clip' ) ;
539
+ return ( element . scrollHeight > element . clientHeight ) ;
552
540
}
541
+ } else { // parameter: element
542
+ return ( element . scrollWidth > element . clientWidth || element . scrollHeight > element . clientHeight ) ;
553
543
}
554
- return false ;
555
- }
556
-
557
- else {
558
- console . log ( 'Need parameters for isScrollable()' ) ;
559
- return false ;
560
- }
561
- }
562
-
563
- /* Whether this element is overflow or not */
564
- function isOverflow ( ) {
565
- // parameter: element
566
- if ( arguments . length == 1 && typeof arguments [ 0 ] === 'object' ) {
567
- const element = arguments [ 0 ] ;
568
- if ( element . scrollWidth > element . clientWidth || element . scrollHeight > element . clientHeight ) {
569
- return true ;
570
- }
571
- else {
572
- return false ;
573
- }
574
- }
575
- // parameter: element, dir
576
- else if ( arguments . length == 2 && typeof arguments [ 0 ] === 'object'
577
- && typeof arguments [ 1 ] === 'string' ) {
578
- const element = arguments [ 0 ] ;
579
- const dir = arguments [ 1 ] ;
580
-
581
- switch ( dir ) {
582
- case 'left' :
583
- /* falls through */
584
- case 'right' :
585
- if ( element . scrollWidth > element . clientWidth )
586
- return true ;
587
- break ;
588
- case 'up' :
589
- /* falls through */
590
- case 'down' :
591
- if ( element . scrollHeight > element . clientHeight )
592
- return true ;
593
- break ;
594
- }
595
- return false ;
596
- }
597
- else {
598
- return false ;
599
544
}
545
+ return false ;
600
546
}
601
547
602
548
/* Check whether the scroll of window is down to the end or not */
@@ -659,19 +605,19 @@ function focusNavigationHeuristics(spatnavPolyfillOptions) {
659
605
* Check whether this element is completely visible in this viewport for the arrow direction.
660
606
*/
661
607
function isEntirelyVisible ( element ) {
662
- const container = element . getSpatnavContainer ( ) ;
663
608
const rect = element . getBoundingClientRect ( ) ;
664
- const containerRect = container . getBoundingClientRect ( ) ;
609
+ const containerRect = element . getSpatnavContainer ( ) . getBoundingClientRect ( ) ;
665
610
666
611
// FIXME: when element is bigger than container?
612
+ const entirelyVisible = ! ( ( rect . left < containerRect . left ) ||
613
+ ( rect . right > containerRect . right ) ||
614
+ ( rect . top < containerRect . top ) ||
615
+ ( rect . bottom > containerRect . botto ) ) ;
667
616
668
- if ( rect . left < containerRect . left ) return false ;
669
- if ( rect . right > containerRect . right ) return false ;
670
- if ( rect . top < containerRect . top ) return false ;
671
- if ( rect . bottom > containerRect . botto ) return false ;
617
+ if ( entirelyVisible )
618
+ console . log ( 'entirely in the view' ) ;
672
619
673
- console . log ( 'entirely in the view' ) ;
674
- return true ;
620
+ return entirelyVisible ;
675
621
}
676
622
677
623
/* Check the style property of this element whether it's visible or not */
@@ -680,7 +626,7 @@ function focusNavigationHeuristics(spatnavPolyfillOptions) {
680
626
const thisDisplay = window . getComputedStyle ( element , null ) . getPropertyValue ( 'display' ) ;
681
627
const invisibleStyle = [ 'hidden' , 'collapse' ] ;
682
628
683
- return ( ! includes ( invisibleStyle , thisVisibility ) && thisDisplay !== 'none' ) ;
629
+ return ( ! invisibleStyle . includes ( thisVisibility ) && thisDisplay !== 'none' ) ;
684
630
}
685
631
686
632
/**
@@ -701,12 +647,12 @@ function focusNavigationHeuristics(spatnavPolyfillOptions) {
701
647
const leftBottomElem = document . elementFromPoint ( elementRect . left + offsetX , elementRect . bottom - offsetY ) ;
702
648
const rightTopElem = document . elementFromPoint ( elementRect . right - offsetX , elementRect . top + offsetY ) ;
703
649
const rightBottomElem = document . elementFromPoint ( elementRect . right - offsetX , elementRect . bottom - offsetY ) ;
704
- if ( element === middleElem || element . contains ( middleElem ) ) return true ;
705
- if ( element === leftTopElem || element . contains ( leftTopElem ) ) return true ;
706
- if ( element === leftBottomElem || element . contains ( leftBottomElem ) ) return true ;
707
- if ( element === rightTopElem || element . contains ( rightTopElem ) ) return true ;
708
- if ( element === rightBottomElem || element . contains ( rightBottomElem ) ) return true ;
709
- return false ;
650
+
651
+ return ( ( element === middleElem || element . contains ( middleElem ) ) ||
652
+ ( element === leftTopElem || element . contains ( leftTopElem ) ) ||
653
+ ( element === leftBottomElem || element . contains ( leftBottomElem ) ) ||
654
+ ( element === rightTopElem || element . contains ( rightTopElem ) ) ||
655
+ ( element === rightBottomElem || element . contains ( rightBottomElem ) ) ) ;
710
656
}
711
657
712
658
/* rect1 is outside of rect2 for the dir */
@@ -953,28 +899,19 @@ function focusNavigationHeuristics(spatnavPolyfillOptions) {
953
899
* https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input
954
900
*/
955
901
function handlingEditableElement ( e ) {
902
+ const spinnableInputTypes = [ 'email' , 'date' , 'month' , 'number' , 'time' , 'week' ] ,
903
+ textInputTypes = [ 'password' , 'text' , 'search' , 'tel' , 'url' ] ;
956
904
const eventTarget = document . activeElement ;
957
905
const startPosition = eventTarget . selectionStart ;
958
906
const endPosition = eventTarget . selectionEnd ;
959
907
const focusNavigableArrowKey = { 'left' : false , 'up' : false , 'right' : false , 'down' : false } ;
960
908
961
- if ( includes ( spinnableInputTypes , eventTarget . getAttribute ( 'type' ) ) ) {
962
- switch ( e . keyCode ) {
963
- case 37 : // left keycode
964
- focusNavigableArrowKey . left = false ;
965
- break ;
966
- case 38 : // up keycode
967
- focusNavigableArrowKey . up = true ;
968
- break ;
969
- case 39 : // right keycode
970
- focusNavigableArrowKey . right = false ;
971
- break ;
972
- case 40 : // down keycode
973
- focusNavigableArrowKey . down = true ;
974
- break ;
975
- }
909
+ const dir = ARROW_KEY_CODE [ e . keyCode ] ;
910
+ if ( spinnableInputTypes . includes ( eventTarget . getAttribute ( 'type' ) ) &&
911
+ ( dir === 'up' || dir === 'down' ) ) {
912
+ focusNavigableArrowKey [ dir ] = true ;
976
913
}
977
- else if ( includes ( textInputTypes , eventTarget . getAttribute ( 'type' ) ) ) {
914
+ else if ( textInputTypes . includes ( eventTarget . getAttribute ( 'type' ) ) ) {
978
915
if ( startPosition === 0 ) {
979
916
focusNavigableArrowKey . left = true ;
980
917
focusNavigableArrowKey . up = true ;
@@ -985,40 +922,15 @@ function focusNavigationHeuristics(spatnavPolyfillOptions) {
985
922
}
986
923
}
987
924
else {
988
- switch ( e . keyCode ) {
989
- case 37 : // left keycode
990
- focusNavigableArrowKey . left = true ;
991
- break ;
992
- case 38 : // up keycode
993
- focusNavigableArrowKey . up = true ;
994
- break ;
995
- case 39 : // right keycode
996
- focusNavigableArrowKey . right = true ;
997
- break ;
998
- case 40 : // down keycode
999
- focusNavigableArrowKey . down = true ;
1000
- break ;
1001
- }
925
+ focusNavigableArrowKey [ dir ] = true ;
1002
926
}
1003
927
1004
928
return focusNavigableArrowKey ;
1005
929
}
1006
930
1007
- /*
1008
- * Nodelist Object Function
1009
- * Whether NodeList includes the element or not
1010
- */
1011
- function includes ( nodelist , element ) {
1012
- for ( let i = 0 ; i < nodelist . length ; i ++ ) {
1013
- if ( nodelist [ i ] === element ) return true ;
1014
- }
1015
- return false ;
1016
- }
1017
-
1018
-
1019
931
// Use non standard names by default, as per https://www.w3.org/2001/tag/doc/polyfills/#don-t-squat-on-proposed-names-in-speculative-polyfills
1020
932
// Allow binding to standard name for testing purposes
1021
- if ( typeof spatnavPolyfillOptions == 'object' && spatnavPolyfillOptions . standardName ) {
933
+ if ( typeof spatnavPolyfillOptions === 'object' && spatnavPolyfillOptions . standardName ) {
1022
934
window . navigate = navigate ;
1023
935
window . Element . prototype . spatNavSearch = spatNavSearch ;
1024
936
window . Element . prototype . focusableAreas = focusableAreas ;
0 commit comments