When creating elements with Javascript they appear below my ‘popup-modal’ form

Totally new to all this, especially Java. Grabbed some code that dynamically creates a Calendar, (works fine standalone), and tried adding it to other code I download which creates something of a modal popup page / form.

When trying to combine the two, the popup displays okay, but the Calendar shows up underneath it and is inaccessible.

I tired looking into the Java code but really not sure what going on.

Hi,

The position of elements on your page is ultimately down to CSS and their stacking level controlled with the z-index property. It may be that the script is outputting inline style rules but it’s still Css that controls how things look.

We’d need to see your whole code or a demo to help explicitly but the answer will most likely lie in setting a higher z-index for the item that is being covered. Z-index applies to positioned elements only (those that are position:relative or absolute or fixed).

Note that Java is another language completely and not to be confused with JavaScript that is in use here. :slight_smile:

Oops sorry didn’t understand there was a difference between Java and JavaScript, thanks for letting me know.

And I have a simple demo along with the code, just have to figure out how to properly upload it. When I try to upload I get a message stating that new users can’t upload attachments.

Would copy and past work? Yikes - that copy and past only kind a worked as it really didn’t want to format the css-style section.

Sorry just seem to be making a mess of this post while trying to get things formatted more cleanly. Below is the style section of the file Index.html

<head>
<style>
body {font-family: Arial, Helvetica, sans-serif;}

/* The Modal (background) */
.modal {
  display: none; /* Hidden by default */
  position: fixed; /* Stay in place */
  z-index: 1; /* Sit on top */
  padding-top: 100px; /* Location of the box */
  left: 0;
  top: 0;
  width: 100%; /* Full width */
  height: 100%; /* Full height */
  overflow: auto; /* Enable scroll if needed */
  background-color: rgb(0,0,0); /* Fallback color */
  background-color: rgba(0,0,0,0.4); /* Black w/ opacity */
}

/* Modal Content */
.modal-content {
  background-color: #fefefe;
  margin: auto;
  padding: 20px;
  border: 1px solid #888;
  width: 80%;
}

/* The Close Button */
.close {
  color: #aaaaaa;
  float: right;
  font-size: 28px;
  font-weight: bold;
}

.close:hover,
.close:focus {
  color: #000;
  text-decoration: none;
  cursor: pointer;
}
</style>

Below is the body of Index.html

    <div class="container">
      <br><br><br>
      <p>Click the button below to open the modal popup.</p>
      <button id="myBtn">Open Modal</button>

      <div id="myModal" class="modal">

        <div class="modal-content">
          <span class="close">&times;</span>

          <p>Click the Input box below to show the calendar.</p>
          <span>Date: <input name="datePicker"/></span>

        </div>

      </div>
    </div>


    <script src="dtsel.js"></script>

    <script>

      var modal = document.getElementById("myModal");
      var btn = document.getElementById("myBtn");
      var span = document.getElementsByClassName("close")[0];


      btn.onclick = function() {
        modal.style.display = "block";
      }


      span.onclick = function() {
        modal.style.display = "none";
      }


      window.onclick = function(event) {
        if (event.target == modal) {
          modal.style.display = "none";
        }
      }


      instance = new dtsel.DTS('input[name="datePicker"]',  {
        direction: 'BOTTOM'
      });
    </script>

  </body>
</html>

And this is the css-style code for the Calendar, the file name should be ‘dtsel.css’. Still not sure why some of this is being formatted differently then others.

.date-selector-wrapper {
    width: 200px;
    padding: 3px;
    background-color: #fff;
    box-shadow: 1px 1px 10px 1px #5c5c5c;
    position: absolute;
    font-size: 12px;
    -webkit-user-select: none;
    -khtml-user-select: none;
    -moz-user-select: none;
    -ms-user-select: none;
    -o-user-select: none;
    /* user-select: none; */
}
.cal-header, .cal-row {
    display: flex;
    width: 100%;
    height: 30px;
    line-height: 30px;
    text-align: center;
}
.cal-cell, .cal-nav {
    cursor: pointer;
}
.cal-day-names {
    height: 25px;
    line-height: 25px;
}
.cal-day-names .cal-cell {
    cursor: default;
    font-weight: bold;
}
.cal-cell-prev, .cal-cell-next {
    color: #777;
}
.cal-months .cal-row, .cal-years .cal-row {
    height: 60px;
    line-height: 60px;
}
.cal-nav-prev, .cal-nav-next {
    flex: 0.15;
}
.cal-nav-current {
    flex: 0.75;
    font-weight: bold;
}
.cal-months .cal-cell, .cal-years .cal-cell {
    flex: 0.25;
}
.cal-days .cal-cell {
    flex: 0.143;
}
.cal-value {
    color: #fff;
    background-color: #286090;
}
.cal-cell:hover, .cal-nav:hover {
    background-color: #eee;
}
.cal-value:hover {
    background-color: #204d74;
}

/* time footer */
.cal-time {
    display: flex;
    justify-content: flex-start;
    height: 27px;
    line-height: 27px;
}
.cal-time-label, .cal-time-value {
    flex: 0.12;
    text-align: center;
}
.cal-time-slider {
    flex: 0.77;
    background-image: linear-gradient(to right, #d1d8dd, #d1d8dd);
    background-repeat: no-repeat;
    background-size: 100% 1px;
    background-position: left 50%;
    height: 100%;
}
.cal-time-slider input {
    width: 100%;
    -webkit-appearance: none;
    background: 0 0;
    cursor: pointer;
    height: 100%;
    outline: 0;
    user-select: auto;
}

And then this is the JavaScript code for the Calendar, the file name should be ‘dtsel.js’. Not sure why it kept the first two lines of code separated, looks as if it did the same with the last line of code as well.


(function () {
    "use strict";

    var BODYTYPES = ["DAYS", "MONTHS", "YEARS"];
    var MONTHS = [
        "January", "February", "March", "April", "May", "June",
        "July", "August", "September", "October", "November", "December"
    ];
    var WEEKDAYS = [
        "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"
    ];

    function DTS(elem, config) {
        var config = config || {};

        /** @type {Config} */
        var defaultConfig = {
            defaultView: BODYTYPES[0],
            dateFormat: "yyyy-mm-dd",
            timeFormat: "HH:MM:SS",
            showDate: true,
            showTime: false,
            paddingX: 5,
            paddingY: 5,
            direction: 'TOP'
        }

        if (!elem) {
            throw TypeError("input element or selector required for contructor");
        }
        if (Object.getPrototypeOf(elem) === String.prototype) {
            var _elem = document.querySelectorAll(elem);
            if (!_elem[0]){
                throw Error('"' + elem + '" not found.');
            }
            elem = _elem[0];
        }
        this.config = setDefaults(config, defaultConfig);
        this.dateFormat = this.config.dateFormat;
        this.timeFormat = this.config.timeFormat;
        this.dateFormatRegEx = new RegExp("yyyy|yy|mm|dd", "gi");
        this.timeFormatRegEx = new RegExp("hh|mm|ss|a", "gi");
        this.inputElem = elem;
        this.dtbox = null;
        this.setup();
    }
    DTS.prototype.setup = function () {
        var handler = this.inputElemHandler.bind(this);
        this.inputElem.addEventListener("focus", handler, false)
        this.inputElem.addEventListener("blur", handler, false);
    }
    DTS.prototype.inputElemHandler = function (e) {
        if (e.type == "focus") {
            if (!this.dtbox) {
                this.dtbox = new DTBox(e.target, this);
            }
            this.dtbox.visible = true;
        } else if (e.type == "blur" && this.dtbox && this.dtbox.visible) {
            var self = this;
            setTimeout(function () {
                if (self.dtbox.cancelBlur > 0) {
                    self.dtbox.cancelBlur -= 1;
                 } else {
                    self.dtbox.visible = false;
                    self.inputElem.blur();
                 }
            }, 100);
        }
    }
    function DTBox(elem, settings) {
        var self = this;
        var handlers = {};
        var localState = {};

        function getterSetter(key, default_val) {
            return {
                get: function () {
                    var val = localState[key];
                    return val === undefined ? default_val : val;
                },
                set: function (val) {
                    var prevState = self.state;
                    var _handlers = handlers[key] || [];
                    localState[key] = val;
                    for (var i = 0; i < _handlers.length; i++) {
                        _handlers[i].bind(self)(localState, prevState);
                    }
                },
            };
        };

        /** @type {AddHandler} */
        function addHandler(key, handlerFn) {
            if (!key || !handlerFn) {
                return false;
            }
            if (!handlers[key]) {
                handlers[key] = [];
            }
            handlers[key].push(handlerFn);
        }

        Object.defineProperties(this, {
            visible: getterSetter("visible", false),
            bodyType: getterSetter("bodyType", settings.config.defaultView),
            value: getterSetter("value"),
            year: getterSetter("year", 0),
            month: getterSetter("month", 0),
            day: getterSetter("day", 0),
            hours: getterSetter("hours", 0),
            minutes: getterSetter("minutes", 0),
            seconds: getterSetter("seconds", 0),
            cancelBlur: getterSetter("cancelBlur", 0),
            addHandler: {value: addHandler},
            month_long: {
                get: function () {
                    return MONTHS[self.month];
                },
            },
            month_short: {
                get: function () {
                    return self.month_long.slice(0, 3);
                },
            },
            state: {
                get: function () {
                    return Object.assign({}, localState);
                },
            },
            time: {
                get: function() {
                    var hours = self.hours * 60 * 60 * 1000;
                    var minutes = self.minutes * 60 * 1000;
                    var seconds = self.seconds * 1000;
                    return  hours + minutes + seconds;
                }
            },
        });
        this.el = {};
        this.settings = settings;
        this.elem = elem;
        this.setup();
    }
    DTBox.prototype.setup = function () {
        Object.defineProperties(this.el, {
            wrapper: { value: null, configurable: true },
            header: { value: null, configurable: true },
            body: { value: null, configurable: true },
            footer: { value: null, configurable: true }
        });
        this.setupWrapper();
        if (this.settings.config.showDate) {
            this.setupHeader();
            this.setupBody();
        }
        if (this.settings.config.showTime) {
            this.setupFooter();
        }

        var self = this;
        this.addHandler("visible", function (state, prevState) {
            if (state.visible && !prevState.visible){
                document.body.appendChild(this.el.wrapper);

                var parts = self.elem.value.split(/\s*,\s*/);
                var startDate = undefined;
                var startTime = 0;
                if (self.settings.config.showDate) {
                    startDate = parseDate(parts[0], self.settings);
                }
                if (self.settings.config.showTime) {
                    startTime = parseTime(parts[parts.length-1], self.settings);
                    startTime = startTime || 0;
                }
                if (!(startDate && startDate.getTime())) {
                    startDate = new Date();
                    startDate = new Date(
                        startDate.getFullYear(),
                        startDate.getMonth(),
                        startDate.getDate()
                    );
                }
                var value = new Date(startDate.getTime() + startTime);
                self.value = value;
                self.year = value.getFullYear();
                self.month = value.getMonth();
                self.day = value.getDate();
                self.hours = value.getHours();
                self.minutes = value.getMinutes();
                self.seconds = value.getSeconds();

                if (self.settings.config.showDate) {
                    self.setHeaderContent();
                    self.setBodyContent();
                }
                if (self.settings.config.showTime) {
                    self.setFooterContent();
                }
            } else if (!state.visible && prevState.visible) {
                document.body.removeChild(this.el.wrapper);
            }
        });
    }
    DTBox.prototype.setupWrapper = function () {
        if (!this.el.wrapper) {
            var el = document.createElement("div");
            el.classList.add("date-selector-wrapper");
            Object.defineProperty(this.el, "wrapper", { value: el });
        }
        var self = this;
        var htmlRoot = document.getElementsByTagName('html')[0];
        function setPosition(e){
            var minTopSpace = 300;
            var box = getOffset(self.elem);
            var config = self.settings.config;
            var paddingY = config.paddingY || 5;
            var paddingX = config.paddingX || 5;
            var top = box.top + self.elem.offsetHeight + paddingY;
            var left = box.left + paddingX;
            var bottom = htmlRoot.clientHeight - box.top + paddingY;

            self.el.wrapper.style.left = `${left}px`;
            if (box.top > minTopSpace && config.direction != 'BOTTOM') {
                self.el.wrapper.style.bottom = `${bottom}px`;
                self.el.wrapper.style.top = '';
            } else {
                self.el.wrapper.style.top = `${top}px`;
                self.el.wrapper.style.bottom = ''; 
            }
        }

        function handler(e) {
            self.cancelBlur += 1;
            setTimeout(function(){
                self.elem.focus();
            }, 50);
        }
        setPosition();
        this.setPosition = setPosition;
        this.el.wrapper.addEventListener("mousedown", handler, false);
        this.el.wrapper.addEventListener("touchstart", handler, false);
        window.addEventListener('resize', this.setPosition);
    }
    DTBox.prototype.setupHeader = function () {
        if (!this.el.header) {
            var row = document.createElement("div");
            var classes = ["cal-nav-prev", "cal-nav-current", "cal-nav-next"];
            row.classList.add("cal-header");
            for (var i = 0; i < 3; i++) {
                var cell = document.createElement("div");
                cell.classList.add("cal-nav", classes[i]);
                cell.onclick = this.onHeaderChange.bind(this);
                row.appendChild(cell);
            }
            row.children[0].innerHTML = "&lt;";
            row.children[2].innerHTML = "&gt;";
            Object.defineProperty(this.el, "header", { value: row });
            tryAppendChild(row, this.el.wrapper);
        }
        this.setHeaderContent();
    }
    DTBox.prototype.setHeaderContent = function () {
        var content = this.year;
        if ("DAYS" == this.bodyType) {
            content = this.month_long + " " + content;
        } else if ("YEARS" == this.bodyType) {
            var start = this.year + 10 - (this.year % 10);
            content = start - 10 + "-" + (start - 1);
        }
        this.el.header.children[1].innerText = content;
    }
    DTBox.prototype.setupBody = function () {
        if (!this.el.body) {
            var el = document.createElement("div");
            el.classList.add("cal-body");
            Object.defineProperty(this.el, "body", { value: el });
            tryAppendChild(el, this.el.wrapper);
        }
        var toAppend = null;
        function makeGrid(rows, cols, className, firstRowClass, clickHandler) {
            var grid = document.createElement("div");
            grid.classList.add(className);
            for (var i = 1; i < rows + 1; i++) {
                var row = document.createElement("div");
                row.classList.add("cal-row", "cal-row-" + i);
                if (i == 1 && firstRowClass) {
                    row.classList.add(firstRowClass);
                }
                for (var j = 1; j < cols + 1; j++) {
                    var col = document.createElement("div");
                    col.classList.add("cal-cell", "cal-col-" + j);
                    col.onclick = clickHandler;
                    row.appendChild(col);
                }
                grid.appendChild(row);
            }
            return grid;
        }
        if ("DAYS" == this.bodyType) {
            toAppend = this.el.body.calDays;
            if (!toAppend) {
                toAppend = makeGrid(7, 7, "cal-days", "cal-day-names", this.onDateSelected.bind(this));
                for (var i = 0; i < 7; i++) {
                    var cell = toAppend.children[0].children[i];
                    cell.innerText = WEEKDAYS[i].slice(0, 2);
                    cell.onclick = null;
                }
                this.el.body.calDays = toAppend;
            }
        } else if ("MONTHS" == this.bodyType) {
            toAppend = this.el.body.calMonths;
            if (!toAppend) {
                toAppend = makeGrid(3, 4, "cal-months", null, this.onMonthSelected.bind(this));
                for (var i = 0; i < 3; i++) {
                    for (var j = 0; j < 4; j++) {
                        var monthShort = MONTHS[4 * i + j].slice(0, 3);
                        toAppend.children[i].children[j].innerText = monthShort;
                    }
                }
                this.el.body.calMonths = toAppend;
            }
        } else if ("YEARS" == this.bodyType) {
            toAppend = this.el.body.calYears;
            if (!toAppend) {
                toAppend = makeGrid(3, 4, "cal-years", null, this.onYearSelected.bind(this));
                this.el.body.calYears = toAppend;
            }
        }
        empty(this.el.body);
        tryAppendChild(toAppend, this.el.body);
        this.setBodyContent();
    }
    DTBox.prototype.setBodyContent = function () {
        var grid = this.el.body.children[0];
        var classes = ["cal-cell-prev", "cal-cell-next", "cal-value"];
        if ("DAYS" == this.bodyType) {
            var oneDayMilliSecs = 24 * 60 * 60 * 1000;
            var start = new Date(this.year, this.month, 1);
            var adjusted = new Date(start.getTime() - oneDayMilliSecs * start.getDay());

            grid.children[6].style.display = "";
            for (var i = 1; i < 7; i++) {
                for (var j = 0; j < 7; j++) {
                    var cell = grid.children[i].children[j];
                    var month = adjusted.getMonth();
                    var date = adjusted.getDate();
                    
                    cell.innerText = date;
                    cell.classList.remove(classes[0], classes[1], classes[2]);
                    if (month != this.month) {
                        if (i == 6 && j == 0) {
                            grid.children[6].style.display = "none";
                            break;
                        }
                        cell.classList.add(month < this.month ? classes[0] : classes[1]);
                    } else if (isEqualDate(adjusted, this.value)){
                        cell.classList.add(classes[2]);
                    }
                    adjusted = new Date(adjusted.getTime() + oneDayMilliSecs);
                }
            }
        } else if ("YEARS" == this.bodyType) {
            var year = this.year - (this.year % 10) - 1;
            for (i = 0; i < 3; i++) {
                for (j = 0; j < 4; j++) {
                    grid.children[i].children[j].innerText = year;
                    year += 1;
                }
            }
            grid.children[0].children[0].classList.add(classes[0]);
            grid.children[2].children[3].classList.add(classes[1]);
        }
    }

    /** @param {Event} e */
    DTBox.prototype.onTimeChange = function(e) {
        e.stopPropagation();
        if (e.type == 'mousedown') {
            this.cancelBlur += 1;
            return;
        }
        
        var el = e.target;
        this[el.name] = parseInt(el.value) || 0;
        this.setupFooter();
        if (e.type == 'change') {
            var self = this;
            setTimeout(function(){
                self.elem.focus();
            }, 50);
        }
        this.setInputValue();
    }

    DTBox.prototype.setupFooter = function() {
        if (!this.el.footer) {
            var footer = document.createElement("div");
            var handler = this.onTimeChange.bind(this);
            var self = this;
            
            function makeRow(label, name, range, changeHandler) {
                var row = document.createElement("div");
                row.classList.add('cal-time');

                var labelCol = row.appendChild(document.createElement("div"));
                labelCol.classList.add('cal-time-label');
                labelCol.innerText = label;

                var valueCol = row.appendChild(document.createElement("div"));
                valueCol.classList.add('cal-time-value');
                valueCol.innerText = '00';

                var inputCol = row.appendChild(document.createElement("div"));
                var slider = inputCol.appendChild(document.createElement("input"));
                Object.assign(slider, {step:1, min:0, max:range, name:name, type:'range'});
                Object.defineProperty(footer, name, {value: slider});
                inputCol.classList.add('cal-time-slider');
                slider.onchange = changeHandler;
                slider.oninput = changeHandler;
                slider.onmousedown = changeHandler;
                self[name] = self[name] || parseInt(slider.value) || 0;
                footer.appendChild(row)
            }
            makeRow('HH:', 'hours', 23, handler);
            makeRow('MM:', 'minutes', 59, handler);
            makeRow('SS:', 'seconds', 59, handler);

            footer.classList.add("cal-footer");
            Object.defineProperty(this.el, "footer", { value: footer });
            tryAppendChild(footer, this.el.wrapper);
        }
        this.setFooterContent();
    }

    DTBox.prototype.setFooterContent = function() {
        if (this.el.footer) {
            var footer = this.el.footer;
            footer.hours.value = this.hours;
            footer.children[0].children[1].innerText = padded(this.hours, 2);
            footer.minutes.value = this.minutes;
            footer.children[1].children[1].innerText = padded(this.minutes, 2);
            footer.seconds.value = this.seconds;
            footer.children[2].children[1].innerText = padded(this.seconds, 2);
        }
    }

    DTBox.prototype.setInputValue = function() {
        var date = new Date(this.year, this.month, this.day);
        var strings = [];
        if (this.settings.config.showDate) {
            strings.push(renderDate(date, this.settings));
        }
        if (this.settings.config.showTime) {
            var joined = new Date(date.getTime() + this.time);
            strings.push(renderTime(joined, this.settings));
        }
        this.elem.value = strings.join(', ');
    }

    DTBox.prototype.onDateSelected = function (e) {
        var row = e.target.parentNode;
        var date = parseInt(e.target.innerText);
        if (!(row.nextSibling && row.nextSibling.nextSibling) && date < 8) {
            this.month += 1;
        } else if (!(row.previousSibling && row.previousSibling.previousSibling) && date > 7) {
            this.month -= 1;
        }
        this.day = parseInt(e.target.innerText);
        this.value = new Date(this.year, this.month, this.day);
        this.setInputValue();
        this.setHeaderContent();
        this.setBodyContent();
    }

    /** @param {Event} e */
    DTBox.prototype.onMonthSelected = function (e) {
        var col = 0;
        var row = 2;
        var cell = e.target;
        if (cell.parentNode.nextSibling){
            row = cell.parentNode.previousSibling ? 1: 0;
        }
        if (cell.previousSibling) {
            col = 3;
            if (cell.nextSibling) {
                col = cell.previousSibling.previousSibling ? 2 : 1;
            }
        }
        this.month = 4 * row + col;
        this.bodyType = "DAYS";
        this.setHeaderContent();
        this.setupBody();
    }

    /** @param {Event} e */
    DTBox.prototype.onYearSelected = function (e) {
        this.year = parseInt(e.target.innerText);
        this.bodyType = "MONTHS";
        this.setHeaderContent();
        this.setupBody();
    }

    /** @param {Event} e */
    DTBox.prototype.onHeaderChange = function (e) {
        var cell = e.target;
        if (cell.previousSibling && cell.nextSibling) {
            var idx = BODYTYPES.indexOf(this.bodyType);
            if (idx < 0 || !BODYTYPES[idx + 1]) {
                return;
            }
            this.bodyType = BODYTYPES[idx + 1];
            this.setupBody();
        } else {
            var sign = cell.previousSibling ? 1 : -1;
            switch (this.bodyType) {
                case "DAYS":
                    this.month += sign * 1;
                    break;
                case "MONTHS":
                    this.year += sign * 1;
                    break;
                case "YEARS":
                    this.year += sign * 10;
            }
            if (this.month > 11 || this.month < 0) {
                this.year += Math.floor(this.month / 11);
                this.month = this.month > 11 ? 0 : 11;
            }
        }
        this.setHeaderContent();
        this.setBodyContent();
    }


    function getOffset(elem) {
        var box = elem.getBoundingClientRect();
        var left = window.pageXOffset !== undefined ? window.pageXOffset : 
            (document.documentElement || document.body.parentNode || document.body).scrollLeft;
        var top = window.pageYOffset !== undefined ? window.pageYOffset : 
            (document.documentElement || document.body.parentNode || document.body).scrollTop;
        return { left: box.left + left, top: box.top + top };
    }
    function empty(e) {
        for (; e.children.length; ) e.removeChild(e.children[0]);
    }
    function tryAppendChild(newChild, refNode) {
        try {
            refNode.appendChild(newChild);
            return newChild;
        } catch (e) {
            console.trace(e);
        }
    }

    function hookFuncs() {
        /** @type {Handlers} */
        this._funcs = {};
    }
    hookFuncs.prototype.add = function(key, func){
        if (!this._funcs[key]){
            this._funcs[key] = [];
        }
        this._funcs[key].push(func)
    }

    hookFuncs.prototype.get = function(key){
        return this._funcs[key] ? this._funcs[key] : [];
    }

    function sortByStringIndex(arr, string) {
        return arr.sort(function(a, b){
            var h = string.indexOf(a);
            var l = string.indexOf(b);
            var rank = 0;
            if (h < l) {
                rank = -1;
            } else if (l < h) {
                rank = 1;
            } else if (a.length > b.length) {
                rank = -1;
            } else if (b.length > a.length) {
                rank = 1;
            }
            return rank;
        });
    }

    function filterFormatKeys(keys, format) {
        var out = [];
        var formatIdx = 0;
        for (var i = 0; i<keys.length; i++) {
            var key = keys[i];
            if (format.slice(formatIdx).indexOf(key) > -1) {
                formatIdx += key.length;
                out.push(key);
            }
        }
        return out;
    }

    function parseData(value, format, formatObj, setHooks) {
        var hooks = {
            canSkip: new hookFuncs(),
            updateValue: new hookFuncs(),
        }
        var keys = sortByStringIndex(Object.keys(formatObj), format);
        var filterdKeys = filterFormatKeys(keys, format);
        var vstart = 0; // value start
        if (setHooks) {
            setHooks(hooks);
        }

        for (var i = 0; i < keys.length; i++) {
            var key = keys[i];
            var fstart = format.indexOf(key);
            var _vstart = vstart; // next value start
            var val = null;
            var canSkip = false;
            var funcs = hooks.canSkip.get(key);

            vstart = vstart || fstart;

            for (var j = 0; j < funcs.length; j++) {
                if (funcs[j](formatObj)){
                    canSkip = true;
                    break;
                }
            }
            if (fstart > -1 && !canSkip) {
                var sep = null;
                var stop = vstart + key.length;
                var fnext = -1;
                var nextKeyIdx = i + 1;
                _vstart += key.length; // set next value start if current key is found

                // get next format token used to determine separator
                while (fnext == -1 && nextKeyIdx < keys.length){
                    var nextKey = keys[nextKeyIdx];
                    nextKeyIdx += 1;
                    if (filterdKeys.indexOf(nextKey) === -1) {
                        continue;
                    }
                    fnext = nextKey ? format.indexOf(nextKey) : -1; // next format start
                }
                if (fnext > -1){
                    sep = format.slice(stop, fnext);
                    if (sep) {
                        var _stop = value.slice(vstart).indexOf(sep);
                        if (_stop && _stop > -1){
                            stop = _stop + vstart;
                            _vstart = stop + sep.length;
                        }
                    }
                }
                val = parseInt(value.slice(vstart, stop));

                var funcs = hooks.updateValue.get(key);
                for (var k = 0; k < funcs.length; k++) {
                    val = funcs[k](val, formatObj, vstart, stop);
                }
            }
            formatObj[key] = { index: vstart, value: val };
            vstart = _vstart; // set next value start
        }
        return formatObj;
    }

    function parseDate(value, settings) {
        /** @type {{yyyy:number=, yy:number=, mm:number=, dd:number=}} */
        var formatObj = {yyyy:null, yy:null, mm:null, dd:null};
        var format = ((settings.dateFormat) || '').toLowerCase();
        if (!format) {
            throw new TypeError('dateFormat not found (' + settings.dateFormat + ')');
        }
        var formatObj = parseData(value, format, formatObj, function(hooks){
            hooks.canSkip.add("yy", function(data){
                return data["yyyy"].value;
            });
            hooks.updateValue.add("yy", function(val){
                return 100 * Math.floor(new Date().getFullYear() / 100) + val;
            });
        });
        var year = formatObj["yyyy"].value || formatObj["yy"].value;
        var month = formatObj["mm"].value - 1;
        var date = formatObj["dd"].value;
        var result = new Date(year, month, date);
        return result;
    }

    function parseTime(value, settings) {
        var format = ((settings.timeFormat) || '').toLowerCase();
        if (!format) {
            throw new TypeError('timeFormat not found (' + settings.timeFormat + ')');
        }

        /** @type {{hh:number=, mm:number=, ss:number=, a:string=}} */
        var formatObj = {hh:null, mm:null, ss:null, a:null};
        var formatObj = parseData(value, format, formatObj, function(hooks){
            hooks.updateValue.add("a", function(val, data, start, stop){
                return value.slice(start, start + 2);
            });
        });
        var hours = formatObj["hh"].value;
        var minutes = formatObj["mm"].value;
        var seconds = formatObj["ss"].value;
        var am_pm = formatObj["a"].value;
        var am_pm_lower = am_pm ? am_pm.toLowerCase() : am_pm;
        if (am_pm && ["am", "pm"].indexOf(am_pm_lower) > -1){
            if (am_pm_lower == 'am' && hours == 12){
                hours = 0;
            } else if (am_pm_lower == 'pm') {
                hours += 12;
            }
        }
        var time = hours * 60 * 60 * 1000 + minutes * 60 * 1000 + seconds * 1000;
        return time;
    }

    function renderDate(value, settings) {
        var format = settings.dateFormat.toLowerCase();
        var date = value.getDate();
        var month = value.getMonth() + 1;
        var year = value.getFullYear();
        var yearShort = year % 100;
        var formatObj = {
            dd: date < 10 ? "0" + date : date,
            mm: month < 10 ? "0" + month : month,
            yyyy: year,
            yy: yearShort < 10 ? "0" + yearShort : yearShort
        };
        var str = format.replace(settings.dateFormatRegEx, function (found) {
            return formatObj[found];
        });
        return str;
    }

    function renderTime(value, settings) {
        var Format = settings.timeFormat;
        var format = Format.toLowerCase();
        var hours = value.getHours();
        var minutes = value.getMinutes();
        var seconds = value.getSeconds();
        var am_pm = null;
        var hh_am_pm = null;
        if (format.indexOf('a') > -1) {
            am_pm = hours >= 12 ? 'pm' : 'am';
            am_pm = Format.indexOf('A') > -1 ? am_pm.toUpperCase() : am_pm;
            hh_am_pm = hours == 0 ? '12' : (hours > 12 ? hours%12 : hours);
        }
        var formatObj = {
            hh: am_pm ? hh_am_pm : (hours < 10 ? "0" + hours : hours),
            mm: minutes < 10 ? "0" + minutes : minutes,
            ss: seconds < 10 ? "0" + seconds : seconds,
            a: am_pm,
        };
        var str = format.replace(settings.timeFormatRegEx, function (found) {
            return formatObj[found];
        });
        return str;
    }

    function isEqualDate(date1, date2) {
        if (!(date1 && date2)) return false;
        return (date1.getFullYear() == date2.getFullYear() && 
                date1.getMonth() == date2.getMonth() && 
                date1.getDate() == date2.getDate());
    }

    function padded(val, pad, default_val) {
        var default_val = default_val || 0;
        var valStr = '' + (parseInt(val) || default_val);
        var diff = Math.max(pad, valStr.length) - valStr.length;
        return ('' + default_val).repeat(diff) + valStr;
    }

    function setDefaults(obj, objDefaults) {
        var keys = Object.keys(objDefaults);
        for (var i=0; i<keys.length; i++) {
            var key = keys[i];
            if (!Object.prototype.hasOwnProperty.call(obj, key)) {
                obj[key] = objDefaults[key];
            }
        }
        return obj;
    }


    window.dtsel = Object.create({},{
        DTS: { value: DTS },
        DTObj: { value: DTBox },
        fn: {
            value: Object.defineProperties({}, {
                empty: { value: empty },
                appendAfter: {
                    value: function (newElem, refNode) {
                        refNode.parentNode.insertBefore(newElem, refNode.nextSibling);
                    },
                },
                getOffset: { value: getOffset },
                parseDate: { value: parseDate },
                renderDate: { value: renderDate },
                parseTime: {value: parseTime},
                renderTime: {value: renderTime},
                setDefaults: {value: setDefaults},
            }),
        },
    });
})();

As I suspected you can just raise the z-index of the modal.

e.g.

.date-selector-wrapper {
  z-index:2;
}

You may find it easier to set up a free codepen account when learning as that makes it easy to post demos of your problem like this:

1 Like

Hey PaulOB can’t thank you enough for the help it work perfectly and I’m looking into CodePen right now.

I wonder if it would be okay to ask you another question about this same code. Currently the Calendar is displayed through what looks to be a ‘listener’, if I understanding things correctly, and again really new to all of this so could be way off in left field.

What I’d like to be able to do is set things up where I could call a JavaScript function to open / display the Calendar every time the modal-popup form is shown and then close / remove the Calendar whenever the popup is closed.

I tried creating my own functions called Cal_Open & Cal_Close in the Dtsel.js file, and while I was able to reference the Input box ID I ran into problems with the ‘this’ object. Again I hope I’m saying all of this correctly. It would appear as if the ‘this’ object is only accessible from within the main Dtsel JavaScript function, so my Cal_Open & Cal_Close routines aren’t able to reference the ‘this’ object.

Then I tried placing my Open & Close functions within the main Dtsel function but then the modal-popup form couldn’t find / reference Cal_Open or Cal_Close. Something of a catch 22!

Then I figured I could cheat by adding code to the main Dtsel function to just automatically open / display the Calendar which sort of worked, only problem is that the Calendar gets displayed even before the modal-popup is shown.

So any chance there’d be a way to get my Cal_Open and Cal_Close function to work and have them callable from the modal-popup?

Thanks.

I’ll leave that for the JS experts around here to answer for now but if the question remains unanswered I’ll be back tomorrow and take a look :slight_smile:

Java is to Javascript as Pen is to Penguin. :wink:

Yikes that’s a large class for a beginner to be importing…

Well… show us your functions and where you’re defining them?
My instinct is to say ‘dont bother opening/closing the calendar, just always have it as part of the modal, and then when the modal hides itself, it’ll hide the calendar also’… but… I may be misunderstanding the situation/desired outcome (which… is what Paul’s codepen does…)

Yes, kind of overwhelmed by the complexity of the class, slowly working through parts of it, but still a lot in there I don’t understand. I’ve been able to make changes here and there, but stumped when trying to call my own function.

As things stand right now the calendar is displayed only after you click on the Input box. If possible I’d like to do exactly as you described, have it open when the modal is displayed and then for it to close when the model closes.

Actually it appears as if the closing part works, I don’t know it the calendar is being closed properly but it does go away when the modal is closed.

Here is the Cal_Open function I wrote

    function Cal_Open() {

      if (!this.dtbox) 
      {
        this.dtbox = new DTBox( document.getElementById("datePicker"), this);
        this.dtbox.visible = true;
      }

    }

The problem is that it can’t be called from the modal as I get an error that it’s undefined and I tried putting it just about everywhere within the Dtsel.js file. I’ve placed it before and after the main function and everywhere in between, just can’t get it to work.

Oh yea, I added the Id=‘datePicker’ tag to the input box so my function could reference it.

Edit:

When adding my Cal_Open code to the DTS.prototype.setup function inside of Dtsel.js, the calendar opens up immediately upon loading the page, so I kind of know my code works, only problem it that with this method it’s opening too soon, even before the modal popup. And that why I’m looking to be able to call my function separately only when needed.

So, the simplest solution that pops to mind is to modify the modal code to set the focus on the input element.

The calendar is bound to the focus event of the input box. So if you trigger that, the calendar’s own code should kick in and handle the opening for you.

Similarly, it’s listening for a blur (the opposite of focus) to close the calendar.

So, you’ve already got a very small function for making the modal appear. Let’s amend that.

<input name="datePicker"/></span>

=>

<input name="datePicker" id="modalcal" /></span>

and then

      btn.onclick = function() {`
        modal.style.display = "block";
      }

=>

      btn.onclick = function() {
        modal.style.display = "block";
        document.getElementById("modalcal").focus();
      }

Give that a whirl.

1 Like

m_hutley - love the idea, certainly a different way of approaching the problem that I never even thought about. And while it does show the calendar immediately when the modal opens, its revealed another issue I hadn’t seen before.

There are to be other input fields added to the modal popup, and once the Input box looses focus the calendar closes even while the modal stays open.

So if I stick with your idea in setting the focus to the input box to open the calendar, then I’m going to have to change how the calendar closes, and well I don’t know how I’d handle that one. Kind of back to where I started.

Tying to think along the same lines as your solutions to open the calendar wondering what other events beside focus and blur that might be used to close the calendar.

I was thinking about triggering the calendar close event when receiving focus on say a second input box that’s hidden. That way I could control the closing of the calendar by setting the focus to this hidden element, but before even trying I’m guessing that it probably wont allow me to set the focus to something that’s hidden.

Going to have to play around with this a bit more, but thanks for the help - opened my eyes to a different way of thinking about the problem.

So here’s what i would do. Again, trying to keep it as simple as possible, but we’re going to have to dive a little deeper.

First thing’s first, let’s find the bind that the calendar’s code is using for the blur event. If you don’t know how to do that, here’s how I found it:
#1: Open the code. In this case, i’m using Paul’s CodePen (Thanks Paul!).
#2: Open the modal, right click on the input box, and choose “Inspect Element” (or whatever your browser’s equivalant is. I’m using Chrome.)
#3: In the inspection window that pops up, check the Event Listeners tab.
#4: Open the blur event bindings. In codepen, there’s an extra binding that has to do with how they run the codepen site itself. In this case, i’m interested in the one bound to input:
image
#5: Click on the source link (The “pen.js:72”, which tells you what file and line the source of the binder is pointing at)

That gives this rather ugly looking code. Remember, we’re only looking at the blur portion.

      var self = this;
      setTimeout(function () {
        if (self.dtbox.cancelBlur > 0) {
          self.dtbox.cancelBlur -= 1;
        } else {
          self.dtbox.visible = false;
          self.inputElem.blur();
        }
      }, 100);

Most of this code is actually some form of in-built timer - if for some reason you’ve set something called cancelBlur in your calendar object, it will delay the closing of the calendar X number of times. Why? Not a clue.
The important lines here are what the code does to its instance when it finally gets around to closing:

          self.dtbox.visible = false;
          self.inputElem.blur();

Well, we’re not going to use the last one, but we’re going to use the first. the dtbox element of the DTS instance has a property called visible, that is a boolean.

We’ve already got a handle for the DTS instance…

instance = new dtsel.DTS('input[name="datePicker"]', {
  direction: "BOTTOM"
});

(That should really have a ‘let’ in front of it, btw.)

So essentially, instance = self.

You should be able to unbind (removeEventListener) the blur event from the input box after setup, and then insert instance.dtbox.visible = false; into your code wherever the event you want to close the box is desired. (Note: You probably want to check if instance.dtbox is null first, to prevent errors.)

1 Like

Hey - Hey, worked perfectly!!!

Killed the blur listener and added the instance.dtbox.visible = false;

You know there’s almost something better then getting this all working which was learning to use the ‘instance’ object / variable to prefix ‘dtbox.visible’. That’s a key piece of information that will go a long way in helping me understand how this code is working. Can’t thank you enough!

3 Likes

Why use the “use strict” and not use let, const and arrow functions?
The term “modal” is what brought me to this thread.
Modal is an attribute of dialog boxes and means that the modal dialog captures the focus and only elements within the modal dialog can be interacted with.
As html 5 now includes the dialog element and using 2 dialog elements would greatly simplify your project,