[PW] Subfiltering in lange lijsten met opties

Pagina: 1
Acties:

Acties:
  • 0 Henk 'm!

  • remyblok
  • Registratie: Oktober 2003
  • Laatst online: 26-09 17:27
Een jaar of wat geleden heb ik al is een poging gedaan om deze feature aan te vragen, echter was 'ie toen ondergebracht in een topic met een andere context. Nu ga ik het nog een keer proberen.

Waar het op neer komt is dat ik wil filteren binnen de lange lijsten die je krijgt bij het klikken op "Alle opties". Het voorbeeld wat ik eerder gebruikte was het CPU-filter voor laptops. In die lijst staan zo'n 200 opties. Dat is zoeken naar een speld in een hooiberg. Wat ik voorstel is het volgende:

Allereerst een plaatje (die nog oude stijl gebruikt).
Afbeeldingslocatie: http://static.tweakers.net/ext/f/hq2kzeGHaoe5fWLo9AzOgPMV/full.png

1: Precies zoals het nu ook al is, standaard lijst met populaire keuzes
2: Alle keuzes zichtbaar gemaakt, extra is nu een tekstveld en een checkbox "Alles Selecteren"
3: Typen in het veld filtert de lijst on-the-fly.
4: Vinkje zetten bij "Alles Selecteren" en dan worden de keuzes ook gechecked, en kan de lijst worden gefilterd.

Ik speel veel met de Custom CSS hier op Tweakers, dit keer heb ik de Javascript aar is aangepakt. Dus hallelujah, hierbij een volledig werkende versie van wat ik bereiken wil :D. Hoop dat jullie je voordeel ermee doen. IIg heb ik een leuke knutsel projectje gehad ;)
JavaScript:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
function FilterFormExtended(filterForm) {
    this.filterForm = filterForm;
    this.originalHandleAjaxResponse = null;
}

Object.extend(FilterFormExtended.prototype, {
    init: function () {
        this.hideMoreLinks();
    },
    hideMoreLinks: function () {
        if (!filterForm.listing) return;
        // find all the links more/less options link
        var links = Selector('.showLink, .hideLink');
        var i = 0, link;
        while ((link = links[i++])) {
            //this is the id of the div element of the current filter
            var filterName = link.parentNode.parentNode.id;

            //just a check to see the scipt is working
            if (window.debug)
                link.innerHTML = link.innerHTML + " extended";

            // we want additional things to happen when the user clicks on the link
            this.addSimpleEvent(link, 'onclick', this.toggleHideMore);

            // creating HTML, could be rendered at the server
            this.generateHtml(link, filterName);

            //select the ul element of the extended filter
            var ul = Selector('#' + filterName + '_extendedFilter', link.parentNode)[0];

            // get the actual filter element
            var filter = Selector('#' + filterName + '_filter', ul)[0];
            filter.onkeyup = this.filterList;

            // get the actual select all element
            var selectAllCheckbox = Selector('#' + filterName + '_selectall', ul)[0];
            selectAllCheckbox.onclick = this.selectAll;

            // create a options bag to store key references into
            var options = {};
            options.filterName = filterName;
            options.filterElement = filter;
            options.filterContainer = ul;
            options.selectAllCheckbox = selectAllCheckbox;

            // figure out what option values there are in the filer
            // we need to exclude the li elements from the extended filter
            options.optionsElements = Selector('li', link.parentNode);
            var j = 0, li;
            while ((li = options.optionsElements[j++])) {
                if (li.parentNode == ul)
                    options.optionsElements.splice(--j, 1);
            }

            filter.options = options;
            selectAllCheckbox.options = options;
            link.options = options;

            // trigger the initial visibility
            this.toggleHideMore.call(link);

        }

        // replace the original handleAjaxResponse method so we can inject our onw code first.
        this.originalHandleAjaxResponse = this.filterForm.handleAjaxResponse;
        this.filterForm.handleAjaxResponse = this.handleAjaxResponse;


    },
    generateHtml: function (link, filterName) {
        var html =
            ('<li>' +
                '<input type="text" id="{filterName}_filter" name="extendedFilter" class="text" placehoder="Filter">' +
            '</li>' +
            '<li>' +
                '<label for="{filterName}_selectall" class="checkbox">' +
                    '<span class="inputWrapper">' +
                        '<input type="checkbox" name="extendedSelectall" id="{filterName}_selectall" value="{filterName}_selectall">' +
                    '</span>' +
                    '<span title="Alles Selecteren" class="facetLabel"><b>Alles Selecteren</b></span>' +
                '</label>' +
            '</li>').replace(/{filterName}/g, filterName);

        // add the a new ul to the page
        // ul used so that is can be easily hidden
        var ul = document.createElement('ul');
        link.parentNode.insertBefore(ul, link.previousSibling.previousSibling); // before the link there ar two ul elements, we place it above these two elements
        ul.id = filterName + "_extendedFilter";
        ul.innerHTML = html;
    },
    handleAjaxResponse: function (response) {
        // validate the data is OK
        var data = checkJsonResponse(response);

        // because the querystring returned from the server does not include the extended field these need to be added
        // otherwise the original handleAjaxResponse will clear the form, and not reset the values
        if ('querystring' in data) {

            // first we do all the select all comboboxes
            var selectAlls = Selector("[name='extendedSelectall']");
            var i = 0, selectAll;

            data.querystring.extendedSelectall = [];
            while ((selectAll = selectAlls[i++])) {
                if (selectAll.checked)
                    data.querystring.extendedSelectall[data.querystring.extendedSelectall.length] = selectAll.value;
            }

            // now the do the user typed filer
            var filters = Selector("[name='extendedFilter']");
            var j = 0, filter;

            // we check if there is a value filled in and then add it to the query string.
            while ((filter = filters[j++])) {
                if (filter.value !== '' && filter.value !== undefined)
                    data.querystring[filter.id] = filter.value;
            }
        }

        // call the original method
        filterFormExtended.originalHandleAjaxResponse.call(this, response);
    },
    // 'this' is the 'a'-element of the show/hide more link
    toggleHideMore: function (e) {
        // because the original code has already run the check is the other way around
        if (this.className != /*==*/ 'showLink') {
            removeClass(this.options.filterContainer, 'hideMore');
        } else {
            addClass(this.options.filterContainer, 'hideMore');
            // reset the filter and make sure all options are visible again
            removeClass(this.options.selectAllCheckbox.parentNode.parentNode, 'selected');
            this.options.selectAllCheckbox.checked = false;
            this.options.filterElement.value = '';
            filterFormExtended.filterList.call(this.options.filterElement, e);
        }
    },
    // onKeyUp event of the filter box
    // 'this' is the 'input'-element text filter
    filterList: function (e) {
        // we don't want to be case sensitive
        var searchString = this.value.toLowerCase();
        var i = 0, li, needsScreenUpdate = false, shownItems = 0, checkedItems = 0;
        // loop over all options within this filter to see if they match
        // if they don't match we hide them, oterwise we show them
        // it also keeps track of the state of the checkbox before it was hidden
        // so then when it is shown again we can reset the correct state
        while ((li = this.options.optionsElements[i++])) {
            var label = singleOrNull(Selector('.facetLabel', li));
            var checkbox = singleOrNull(Selector('input', li));
            if (label !== null && checkbox !== null) {
                // do the string comparision, also here lower case
                if (label.innerText.toLowerCase().contains(searchString)) {
                    // if it is currently hidden, we need to make it visible
                    if (li.className == 'hideMore') {
                        removeClass(li, 'hideMore');
                        // restore the state of check box from before the filter
                        if (checkbox.checkedBeforeFilter)
                            checkbox.checked = checkbox.checkedBeforeFilter;
                        checkbox.checkedBeforeFilter = checkbox.checked;
                        // if a checked option is now visible we need to refresh the screen
                        needsScreenUpdate |= checkbox.checked;
                    }
                    shownItems++;

                    if (checkbox.checked)
                        checkedItems++;
                }
                    // only hide it if it is not already hidden
                    // otherwise we override the checkedBeforeFilter state
                else if (li.className != 'hideMore') {
                    addClass(li, 'hideMore');
                    // if a checked option is now hidden we need to refresh the screen
                    needsScreenUpdate |= checkbox.checked;
                    checkbox.checkedBeforeFilter = checkbox.checked;
                    // make sure the option in no longer checked
                    checkbox.checked = false;
                }
            }
        }
        // finally update the page with the selected options if needed
        if (needsScreenUpdate)
            filterForm.ajaxTimer();

        // disable the select all button if there are no options
        this.options.selectAllCheckbox.disabled = shownItems === 0;

        // show the selected all button as selected if all filter options are selected
        if (shownItems > 0 && checkedItems == shownItems) {
            addClass(this.options.selectAllCheckbox.parentNode.parentNode, 'selected');
            this.options.selectAllCheckbox.checked = true;
        } else {
            removeClass(this.options.selectAllCheckbox.parentNode.parentNode, 'selected');
            this.options.selectAllCheckbox.checked = false;
        }
    },
    // onClick event of the selected all combobox
    // 'this' is sthe 'input'-element combobox
    selectAll: function (e) {
        var i = 0, li, needsScreenUpdate = false;
        // loop over all options, check if there are not hidden due to the text filter
        // then check the options
        while ((li = this.options.optionsElements[i++])) {
            if (!this.checked || li.className != 'hideMore') {
                var checkbox = singleOrNull(Selector('input', li));
                if (checkbox !== null) {
                    // only update the screen if the checkbox is switched from state
                    needsScreenUpdate |= (checkbox.checked != this.checked);
                    checkbox.checked = this.checked;
                }
            }
        }
        // finally update the page with the selected options if needed
        if (needsScreenUpdate)
            filterForm.ajaxTimer();
    },
    // fixed helper funtion available in T.net code
    // now actually supplies the eventargs to the functions
    addSimpleEvent: function (element, event, func) {
        if (element[event]) {
            var oldFunction = element[event];
            element[event] = function(e) {
                oldFunction.call(this, e);
                func.call(this, e);
            };
        } else element[event] = func;
    }
});

// little helper function to get the first element from an array
function singleOrNull(array) {
    if (Array.isArray(array) && array.length == 1)
        return array[0];
    return null;
}


window.debug = true;
// init the code
if (filterForm) {
    var filterFormExtended = new FilterFormExtended(filterForm);
    filterFormExtended.init();
}

[ Voor 0% gewijzigd door remyblok op 08-06-2015 13:30 . Reden: Wat puntcomma's toegevoegd ]


Acties:
  • 0 Henk 'm!

  • AW_Bos
  • Registratie: April 2002
  • Laatst online: 20:48

AW_Bos

Liefhebber van nostalgie... 🕰️

Volkomen eens!

Tip: Maak er een Stylish of Greasemonkey-addon voor! :)

Telecommunicatie van vroeger
🚅Alles over spoor en treintjes


Acties:
  • 0 Henk 'm!

  • NitroX infinity
  • Registratie: Januari 2002
  • Laatst online: 16:49

Graphene; a material that can do everything, except leave the lab. - Asianometry