1+ class FilterContainer extends HTMLElement {
2+ constructor ( ) {
3+ super ( ) ;
4+ this . attrs = {
5+ bind : "data-filter-bind" ,
6+ delimiter : "data-filter-delimiter" ,
7+ results : "data-filter-results" ,
8+ } ;
9+ this . classes = {
10+ hidden : "filter--hide" ,
11+ }
12+ }
13+
14+ connectedCallback ( ) {
15+ this . results = this . querySelector ( `[${ this . attrs . results } ]` ) ;
16+ let formElements = this . getAllFormElements ( ) ;
17+ this . bindEvents ( formElements ) ;
18+ this . filterAll ( formElements ) ;
19+ }
20+
21+ getAllFormElements ( ) {
22+ return this . querySelectorAll ( `[${ this . attrs . bind } ]` ) ;
23+ }
24+
25+ getAllKeys ( ) {
26+ let keys = new Set ( ) ;
27+ for ( let formEl of this . getAllFormElements ( ) ) {
28+ keys . add ( formEl . getAttribute ( this . attrs . bind ) ) ;
29+ }
30+ return Array . from ( keys ) ;
31+ }
32+
33+ getElementSelector ( key ) {
34+ return `data-filter-${ key } `
35+ }
36+
37+ getAllFilterableElements ( ) {
38+ let keys = this . getAllKeys ( ) ;
39+ let selector = keys . map ( key => {
40+ return `[${ this . getElementSelector ( key ) } ]` ;
41+ } ) . join ( "," ) ;
42+ return this . querySelectorAll ( selector ) ;
43+ }
44+
45+ bindEvents ( formElements ) {
46+ for ( let el of formElements ) {
47+ el . addEventListener ( "change" , e => {
48+ this . filter ( e . target ) ;
49+ requestAnimationFrame ( ( ) => {
50+ this . renderResultCount ( ) ;
51+ } )
52+ } , false ) ;
53+ }
54+ }
55+
56+ filterAll ( formElements ) {
57+ for ( let el of formElements ) {
58+ this . filter ( el ) ;
59+ }
60+ this . renderResultCount ( ) ;
61+ }
62+
63+ filter ( formElement ) {
64+ let key = formElement . getAttribute ( this . attrs . bind ) ;
65+ let delimiter = formElement . getAttribute ( this . attrs . delimiter ) ;
66+
67+ let value = formElement . value ;
68+ let elementsSelectorAttr = this . getElementSelector ( key ) ;
69+
70+ let elements = this . querySelectorAll ( `[${ elementsSelectorAttr } ]` ) ;
71+ let count = 0 ;
72+ let cls = `filter-${ key } --hide` ;
73+ for ( let element of Array . from ( elements ) ) {
74+ if ( this . elementIsValid ( element , elementsSelectorAttr , value , delimiter ) ) {
75+ element . classList . remove ( cls ) ;
76+ } else {
77+ element . classList . add ( cls ) ;
78+ }
79+ }
80+ }
81+
82+ elementIsValid ( element , attributeName , value , delimiter ) {
83+ if ( ! value && element . hasAttribute ( attributeName ) ) {
84+ return true ;
85+ }
86+ let attrValue = element . getAttribute ( attributeName ) ;
87+ if ( delimiter && attrValue . split ( delimiter ) . indexOf ( value ) > - 1 ) {
88+ return true ;
89+ }
90+ if ( ! delimiter && attrValue === value ) {
91+ return true ;
92+ }
93+ return false ;
94+ }
95+
96+ elementIsVisible ( element ) {
97+ for ( let cls of element . classList ) {
98+ if ( cls . startsWith ( "filter-" ) && cls . endsWith ( "--hide" ) ) {
99+ return false ;
100+ }
101+ }
102+ return true ;
103+ }
104+
105+ renderResultCount ( ) {
106+ let count = Array . from ( this . getAllFilterableElements ( ) )
107+ . filter ( entry => this . elementIsVisible ( entry ) )
108+ . length ;
109+
110+ this . results . innerHTML = `${ count } Result${ count !== 1 ? "s" : "" } ` ;
111+ }
112+ }
113+
114+ window . customElements . define ( "filter-container" , FilterContainer ) ;
0 commit comments