0
\$\begingroup\$

Is there a more efficient/performant way to accomplish this with vanilla js (no libs please) as the looping is slow? Also, how can I make it more flexible, so the HTML option values can vary a little, if need be?

I need to cover different option values in HTML but they mostly follow the same format. Here are some other examples:

<option value="10/17/2016, 5:00pm - Fort Worth" data-ana-label="Selected - Fort Worth - 10/17, 5:00pm">Fort Worth - 10/17, 5:00pm</option>

<option value="March 04, 2016 - Some Meta Info">March 04, 2016 - Some Meta Info</option>

Any suggestions or guidance would help.

var Remove = function (element, useAnimation, all) {
    /**
     * @author (@colecmc)
     * @version 1.0.0
     * @method Remove all children from element
     * @requires Modernizr
     * @param {object} element - (HTMLElement)
     * @param {boolean} useAnimation - if false, element will be hidden w/o animation
     * @param {boolean} all - remove element along with children
     * @example Remove(container);
     * @example Remove(container, false, true);
     */
    'use strict';

    function remove(target) {
        if (all && target.parentElement) {
            /** remove entire element */
            target.parentElement.removeChild(target);
            return all;
        } else {
            /** remove children */
            while (target && target.hasChildNodes()) {
                target.removeChild(target.lastChild);
            }

            if (useAnimation) {
                /** remove classes for animation */
                target.classList.remove('animated', 'fadeIn');
            } else {
                /** just change visibility */
                target.classList.remove('hidden');
            }
        }
    }

    if (useAnimation && document.documentElement.classList.contains('cssanimations')) {
        /**
         * if Modernizr says css animations are supported
         * start animation and listen for completion
         */
        element.classList.add('animated', 'fadeOut');
        element.addEventListener('animationend', function (event) {
            remove(event.target);
        }, false);
    } else {
        remove(element);
    }
};

/** \/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/ **/


var RemovePastDates = function (collection, withinXDays) {
    /**
     * @author (@colecmc)
     * @version 0.1.0
     * @param {object} collection - array of select elements that include date in id
     * @param {number} withinXDays
     * @requires Remove
     * Get today's date and compare it with each date in list of option elements.
     * Remove date if it is past or within X number of days.
     * Don't remove value of default option and allow all future dates to remain.
     * @returns {object} - arrays of all dates, past dates and future dates.
     * @example - option value should be formatted as such:
     * "September 16, 2016 - Some meta info" or "10/08/2016, 6:30pm - More data"
     * @example RemovePastDates([...document.querySelectorAll('.dates')], 7);
     */

    'use strict';

function onMapValues(item) {
    var itemValue = item.value,
        itemWithDash = itemValue.indexOf(' - '),
        itemWithDate = itemValue.indexOf('_dates=');

    if (itemWithDash !== -1 && itemWithDate === -1) {
        return itemValue.slice(0, itemWithDash);
    } else if (itemWithDate !== -1 && itemWithDash !== -1) {
        return itemValue.slice(itemWithDate + 7, itemWithDash);
    }
}


var now = new Date(),
    month = now.getMonth(),
    year = now.getFullYear(),
    has = i => i !== null && i !== undefined && i !== '',

    list = collection
        .map(item => [...item.options])
        .reduce((a,b) => a.concat(b)),

    dates = list.map(onMapValues).filter(item => !isNaN(Number(item.slice(-2)))),

        /** anytime before now is in the past */
        pastDates = dates.filter(item => new Date(item) <= now),

        /** anytime after now and within x days is in the future */
        futureDates = dates.filter(item => new Date(item) >= new Date(year, month, now.getDate() + withinXDays)),

        diffs = list.map(function(item){
            /** find the difference between the list of dates and actual options */
            var items = futureDates.map(function(thing){
                /** only return the item if these expressions are true */
                if (item.value === 'NoneSelected' || item.value.includes(thing)) {
                    return item;
                }
            });

            return items.filter(has);
        }).map(item => item[0]).filter(has);

    collection.map(function(item){
        /** remove all dates and put back the ones we want to keep */
        Remove(item, false);
        diffs.map(thing => item.insertAdjacentHTML('beforeend', thing.outerHTML));
        return item;
    });

    return {dates,pastDates,futureDates};
};

/** Check for an id with _date and do nothing if it does not exist */
if (document.querySelectorAll('select[id*="_date"]').length >= 1) {
    RemovePastDates([...document.querySelectorAll('select[id*="_date"]')], 5);
}

/** \/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/ **/
<h2 class="callout-title">Schedule a Tour</h2>
<fieldset><legend style="background: none;">Please select the date you'd like to visit</legend><select id="tour_dates" class="selection mb0">
<option id="date-10-11-16-10" value="/some/path/about/tours.html?tour_dates=October 11, 2016, 10:00 - Tour">Tour October 11 @ 10 p.m.</option>
<option id="date-10-12-16-12" value="/some/path/about/tours.html?tour_dates=October 12, 2016, 12:00 - Tour">Tour October 12 @ 12 p.m.</option>
<option id="date-10-13-16-10" value="/some/path/about/tours.html?tour_dates=October 13, 2016, 10:00 - Tour">Tour October 13 @ 10 p.m.</option>
<option id="date-10-14-16-12" value="/some/path/about/tours.html?tour_dates=October 14, 2016, 12:00 - Tour">Tour October 14 @ 12 p.m.</option>
<option id="date-10-15-16-14" value="/some/path/about/tours.html?tour_dates=October 15, 2016, 14:00 - Tour">Tour October 15 @ 14 p.m.</option>
<option id="date-10-16-16-12" value="/some/path/about/tours.html?tour_dates=October 16, 2016, 12:00 - Tour">Tour October 16 @ 12 p.m.</option>
<option id="date-10-17-16-16" value="/some/path/about/tours.html?tour_dates=October 17, 2016, 16:00 - Tour">Tour October 17 @ 16 p.m.</option>
<option id="date-10-18-16-12" value="/some/path/about/tours.html?tour_dates=October 18, 2016, 12:00 - Tour">Tour October 18 @ 12 p.m.</option>
<option id="date-10-18-16-12" value="/some/path/about/tours.html?tour_dates=October 18, 2016, 12:00 - Tour">Tour October 18 @ 12 p.m.</option>
<option id="date-10-19-16-12" value="/some/path/about/tours.html?tour_dates=October 19, 2016, 12:00 - Tour">Tour October 19 @ 12 p.m.</option>
<option id="date-10-20-16-12" value="/some/path/about/tours.html?tour_dates=October 20, 2016, 12:00 - Tour">Tour October 20 @ 12 p.m.</option>
<option id="date-10-21-16-15" value="/some/path/about/tours.html?tour_dates=October 21, 2016, 15:00 - Tour">Tour October 21 @ 03 p.m.</option>
<option id="date-10-25-16-12" value="/some/path/about/tours.html?tour_dates=October 25, 2016, 12:00 - Tour">Tour October 25 @ 12 p.m.</option>
<option id="date-10-25-16-16" value="/some/path/about/tours.html?tour_dates=October 25, 2016, 16:00 - Tour">Tour October 25 @ 04 p.m.</option>
<option id="date-10-26-16-12" value="/some/path/about/tours.html?tour_dates=October 26, 2016, 12:00 - Tour">Tour October 26 @ 12 p.m.</option>
<option id="date-10-27-16-12" value="/some/path/about/tours.html?tour_dates=October 27, 2016, 12:00 - Tour">Tour October 27 @ 12 p.m.</option>
<option id="date-10-28-16-10" value="/some/path/about/tours.html?tour_dates=October 28, 2016, 10:00 - Tour">Tour October 28 @ 10 p.m.</option>
<option id="date-10-28-16-12" value="/some/path/about/tours.html?tour_dates=October 28, 2016, 12:00 - Tour">Tour October 28 @ 12 p.m.</option>
<option id="date-10-31-16-12" value="/some/path/about/tours.html?tour_dates=October 31, 2016, 12:00 - Tour">Tour October 31 @ 12 p.m.</option>
<option id="date-10-31-16-15" value="/some/path/about/tours.html?tour_dates=October 31, 2016, 15:00 - Tour">Tour October 31 @ 03 p.m.</option>
<option id="date-11-01-16-09" value="/some/path/about/tours.html?tour_dates=November 01, 2016, 09:00 - Tour">Tour November 1 @ 09 a.m.</option>
<option id="date-11-01-16-12" value="/some/path/about/tours.html?tour_dates=November 01, 2016, 12:00 - Tour">Tour November 1 @ 12 p.m.</option>
<option id="date-11-01-16-15" value="/some/path/about/tours.html?tour_dates=November 01, 2016, 15:00 - Tour">Tour November 1 @ 03 p.m.</option>
<option id="date-11-02-16-12" value="/some/path/about/tours.html?tour_dates=November 02, 2016, 12:00 - Tour">Tour November 2 @ 12 p.m.</option>
<option id="date-11-03-16-12" value="/some/path/about/tours.html?tour_dates=November 03, 2016, 12:00 - Tour">Tour November 3 @ 12 p.m.</option>
<option id="date-11-04-16-12" value="/some/path/about/tours.html?tour_dates=November 04, 2016, 12:00 - Tour">Tour November 4 @ 12 p.m.</option>
<option id="date-11-04-16-15" value="/some/path/about/tours.html?tour_dates=November 04, 2016, 15:00 - Tour">Tour November 4 @ 03 p.m.</option>
<option id="date-11-05-16-12" value="/some/path/about/tours.html?tour_dates=November 05, 2016, 12:00 - Tour">Tour November 5 @ 12 p.m.</option>
<option id="date-11-06-16-12" value="/some/path/about/tours.html?tour_dates=November 06, 2016, 12:00 - Tour">Tour November 6 @ 12 p.m.</option>
<option id="date-11-07-16-12" value="/some/path/about/tours.html?tour_dates=November 07, 2016, 12:00 - Tour">Tour November 7 @ 12 p.m.</option>
<option id="date-11-08-16-10" value="/some/path/about/tours.html?tour_dates=November 08, 2016, 10:00 - Tour">Tour November 8 @ 10 a.m.</option>
<option id="date-11-08-16-12" value="/some/path/about/tours.html?tour_dates=November 08, 2016, 12:00 - Tour">Tour November 8 @ 12 p.m.</option>
<option id="date-11-08-16-15" value="/some/path/about/tours.html?tour_dates=November 08, 2016, 15:00 - Tour">Tour November 8 @ 03 p.m.</option>
<option id="date-11-10-16-10" value="/some/path/about/tours.html?tour_dates=November 10, 2016, 10:00 - Tour">Tour November 10 @ 10 a.m.</option>
<option id="date-11-10-16-12" value="/some/path/about/tours.html?tour_dates=November 10, 2016, 12:00 - Tour">Tour November 10 @ 12 p.m.</option>
<option id="date-11-10-16-15" value="/some/path/about/tours.html?tour_dates=November 10, 2016, 15:00 - Tour">Tour November 10 @ 03 p.m.</option>
<option id="date-11-11-16-12" value="/some/path/about/tours.html?tour_dates=November 11, 2016, 12:00 - Tour">Tour November 11 @ 12 p.m.</option>
<option id="date-11-11-16-15" value="/some/path/about/tours.html?tour_dates=November 11, 2016, 15:00 - Tour">Tour November 11 @ 03 p.m.</option>
<option id="date-11-14-16-12" value="/some/path/about/tours.html?tour_dates=November 14, 2016, 12:00 - Tour">Tour November 14 @ 12 p.m.</option>
<option id="date-11-15-16-12" value="/some/path/about/tours.html?tour_dates=November 15, 2016, 12:00 - Tour">Tour November 15 @ 12 p.m.</option>
<option id="date-11-15-16-16" value="/some/path/about/tours.html?tour_dates=November 15, 2016, 16:00 - Tour">Tour November 15 @ 04 p.m.</option>
<option id="date-11-16-16-12" value="/some/path/about/tours.html?tour_dates=November 16, 2016, 12:00 - Tour">Tour November 16 @ 12 p.m.</option>
<option id="date-11-17-16-12" value="/some/path/about/tours.html?tour_dates=November 17, 2016, 12:00 - Tour">Tour November 17 @ 12 p.m.</option>
<option id="date-11-17-16-15" value="/some/path/about/tours.html?tour_dates=November 17, 2016, 15:00 - Tour">Tour November 17 @ 03 p.m.</option>
<option id="date-11-18-16-10" value="/some/path/about/tours.html?tour_dates=November 18, 2016, 10:00 - Tour">Tour November 18 @ 10 a.m.</option>
<option id="date-11-18-16-12" value="/some/path/about/tours.html?tour_dates=November 18, 2016, 12:00 - Tour">Tour November 18 @ 12 p.m.</option>
<option id="date-11-21-16-12" value="/some/path/about/tours.html?tour_dates=November 21, 2016, 12:00 - Tour">Tour November 21 @ 12 p.m.</option>
<option id="date-11-22-16-12" value="/some/path/about/tours.html?tour_dates=November 22, 2016, 12:00 - Tour">Tour November 22 @ 12 p.m.</option>
<option id="date-11-22-16-16" value="/some/path/about/tours.html?tour_dates=November 22, 2016, 16:00 - Tour">Tour November 22 @ 04 p.m.</option>
<option id="date-11-23-16-10" value="/some/path/about/tours.html?tour_dates=November 23, 2016, 10:00 - Tour">Tour November 23 @ 10 a.m.</option>
<option id="date-11-23-16-12" value="/some/path/about/tours.html?tour_dates=November 23, 2016, 12:00 - Tour">Tour November 23 @ 12 p.m.</option>
<option id="date-11-28-16-12" value="/some/path/about/tours.html?tour_dates=November 28, 2016, 12:00 - Tour">Tour November 28 @ 12 p.m.</option>
<option id="date-11-29-16-12" value="/some/path/about/tours.html?tour_dates=November 29, 2016, 12:00 - Tour">Tour November 29 @ 12 p.m.</option>
<option id="date-11-29-16-16" value="/some/path/about/tours.html?tour_dates=November 29, 2016, 16:00 - Tour">Tour November 29 @ 04 p.m.</option>
<option id="date-11-30-16-12" value="/some/path/about/tours.html?tour_dates=November 30, 2016, 12:00 - Tour">Tour November 30 @ 12 p.m.</option>
<option id="date-11-30-16-16" value="/some/path/about/tours.html?tour_dates=November 30, 2016, 16:00 - Tour">Tour November 30 @ 04 p.m.</option>
<option id="date-12-01-16-14" value="/some/path/about/tours.html?tour_dates=December 01, 2016, 14:00 - Tour">Tour December 01 @ 02 p.m.</option>

</select></fieldset>

\$\endgroup\$
4
  • 1
    \$\begingroup\$ You know you can use the built-in browser performance analysis tools: devtools profiler and timeline panels to identify the bottlenecks. Then a solution might be instantly obvious. \$\endgroup\$
    – woxxom
    Commented Oct 19, 2016 at 17:35
  • \$\begingroup\$ I've looked through the profiler and nothing is jumping out as instantly obvious. \$\endgroup\$
    – colecmc
    Commented Oct 19, 2016 at 18:25
  • 1
    \$\begingroup\$ Profiler is just one tool, and what about the timeline? The thing is your snippet doesn't run, so I can't measure it myself, which leaves the only option: to guess what's causing the problem. \$\endgroup\$
    – woxxom
    Commented Oct 19, 2016 at 19:37
  • \$\begingroup\$ I updated the question and code. It should run now. \$\endgroup\$
    – colecmc
    Commented Oct 20, 2016 at 16:11

1 Answer 1

2
\$\begingroup\$

First, it is really unclear from your example what the point of the removing select options is, as the example does not have any dates in the past. I would guess that this list of options is being created somewhere, and maybe wherver that logic lies is the proper place to address this problem, rather than first adding and then removing options from the select. What if you just never add inappropriate dates to begin with? This problem just goes away.

That being said, let me provide some feedback or what you have:


Use single class for these options to use as a selector rather than your convoluted id-inspection approach. Your approach of storing info in the id adds no value and potentially makes it more difficult to work with these items as a collection.

Also, consider storing an actual string data value in option value. You are getting nothing from storing this whole long URI in value that only differs between options based on the actual data and time.

That might have your HTML looking like this.

<option class="hidable-date-option" value="2016-10-19T12:00:00" ... >
    Tour October 19 @ 12 p.m.</option>

Now your option "value" is actually something meaningful, holding a string that can be used to directly instantiate a Date object.


Now on the javascript side, let's first start with your method names. There don't seems to match to what the functions actually do. For example, RemovePastDates might best be called removeOutOfRangeDatesOptions.

I would consider a function signature like this:

function removeOutOfRangeDates(selector, startDate, endDate, callback) { ... }

where selector contains you query selector (you could pass collection instead, but since this is really only one line of code to instantiate collectoin from selector, I am just using selector here). startDate and endDate could contains javascript data objects, and callback could be callback function to actually perform the "hide" behavior, decoupling the filtering logic from the display logic.

function removeOutOfRangeDates(selector, startDate, endDate, callback) {
    // validate input
    // first see if we can create a valid NodeList
    var options = document.querySelectorAll(selector);
    if (options.length === 0) {
        throw new Error('Selector passed to removeOutOfRangeDates did not match any nodes.');
    }
    if(!startDate instanceof Date) {
        throw new Error('...');
    }
    if(!endDate instanceof Date) {
        throw new Error('...');
    }
    if(typeof callback !== 'function') {
        // maybe throw error or fallback to default callback behavior (simple hide)
    }

    for(var i=0, len = options.length; i < len; i++) {
        var opt = options[i];
        var optDate = new Date(opt.value);
        if (optDate < currentDate || optDate > cutoffDate) {
            // we want to remove this item
            callback(opt);
        }
    }
}   

Now in your document.ready handler you would simply do something like this:

// define configuration to be used
var dateOptionSelector = '.hidable-date-option';
var withinXdays = 5;

// define date range your are wanting to retain
var startDate = new Date();
// note `Date` object will automatically handle days values greater than
// that allowed in the month, rolling date over to next month
var cutOffInt = startDate.getDate() + withinXdays;
var endDate = new Date().setDate(cutOffInt);

var hideOptionElement = function(element) {
    // your callback for hiding an option
}

// execute
removeOutOfRangeDates(selector, startDate, endDate, hideOptionElement);

I frankly don't understand the parent child stuff in your hide function. Are you expecting multiple nesting levels?


There is a lot of logic in your main RemovePastDates method where you are performing map-reduce, filtering, more mapping that I frankly don't understand why you are doing it. This seems like a pretty straightforward problem that you have complicated somehow. The returns from this method don't seem to make sense (why is there a return at all? and if so, why return potentially return one of many different structures?).

\$\endgroup\$
1
  • \$\begingroup\$ Thanks for your feed back Mike. It would be ideal to just start with the correct dates but in my case I can't quickly and easily modify the various forms with these dates. I will try your approach. Thank you again. \$\endgroup\$
    – colecmc
    Commented Oct 20, 2016 at 16:22

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.