NAce01
NAce01

Reputation: 57

Still Having Issues Displaying Select Option in JS

I'm trying to get the selected option from an HTML select box to display on a row in a table element in HTML. I know I asked this question earlier, but after updating the code and trying to make it work, I still can't figure it out. This code generates an HTML form called Create Event.html which enables a user to create a calendar event. This code from Create Event.js is supposed to generate another form within Create Event.html to set a reminder, such as when to send the reminder and who to send it to. After setting a reminder, it shows up on a list of reminders that you can continue to add to. Should have "Before" on "On the day" in the When column There should be "Before" or "On the day" in the When column, but it doesn't appear. I continue to get this error whenever I run the code: "Uncaught Error: Cannot read properties of undefined (reading 'length')" at line 223. I'm not sure what the undefined object is: I believe it is either this.num or this.unit, but I may be wrong. I appreciate any help you can give.

var br = document.createElement("br");

const dom = {
    $(id) {
        if (typeof id === 'string')
            return document.getElementById(id)
        return id
    },
    add_event(el, type, f) {
        el = dom.$(el)
        if (!el.$events)
            el.$events = {}
        el.$events[type] = (el.$events[type] || []).push(f)
        el.addEventListener(type, f)
    },
    add_script(js) {
        if (!js)
            return
        var script = document.createElement('script')
        script.setAttribute('type', 'text/javascript')
        script.text = js
        document.head.appendChild(script)
        document.head.removeChild(script)
    },
    append_text(el, text) {
        el = dom.$(el)
        if (text)
            el.appendChild(document.createTextNode(text))
        return el
    },
    at_bottom() {
        return window.pageYOffset > document.body.clientHeight - window.innerHeight - 10
    },
    children_height(el) {
        let h = 0
        for (var i=0; i<el.children.length; i++)
            h += el.children[i].offsetHeight
        return h
    },
    el(tag, properties) {
        let el = document.createElement(tag)
        for (const p in properties)
            if (!dom.set(el, p, properties))
                el[p] = properties[p]
                //el.setAttribute(p, properties[p])
        return el
    },
    empty(el) {
        el = dom.$(el)
        while (el.firstChild)
            el.removeChild(el.firstChild)
        return el
    },
    extract_js(html) {
        let js = ''
        html = html.replace(/<script[^>]*>([\s\S]*?)<\/script>/gi, function(all, code) {
            js += code + '\n'
            return ''
        })
        return {html:html, js:js}
    },
    pos(el) {
        el = dom.$(el)
        let r = el.getBoundingClientRect()
        return {x:r.left + window.pageXOffset, y:r.top + window.pageYOffset}
    },
    remove_class(el, c) {
        el.classList.remove(c)
        if (el.classList.length == 0)
            el.removeAttribute('class')
    },
    remove_events(el, type) {
        el = dom.$(el)
        if (!el.$events)
            return
        el.$events[type].forEach(f => el.removeEventListener(type, f))
        delete el.$events[type]
    },
    replace(el, html) {
        el = dom.$(el)
        let x = dom.extract_js(html)
        let is_tr = x.html.startsWith('<tr')
        let d = dom.el(is_tr ? 'table' : 'div', {html:x.html})
        el.parentNode.replaceChild(is_tr ? d.querySelector('tr'): d.firstChild, el)
        if (x.js)
            dom.add_script(x.js)
    },
    rotate(el, deg) {
        el = dom.$(el)
        let rotated = el.style.transform
        el.style.transform = rotated ? '' : 'rotate('+(deg?deg:'90')+'deg)'
        return !rotated
    },
    set(el, p, properties) {
        if (p.startsWith('data-') || p == 'for' || p == 'required') {
            el.setAttribute(p, properties[p])
            return true
        }
        switch(p) {
        case 'before':
            properties.before.parentNode.insertBefore(el, properties.before)
            return true
        case 'children':
            for (var i=0; i<properties.children.length; i++)
                el.appendChild(properties.children[i])
            return true;
        case 'classes':
            if (properties.classes)
                el.classList.add(...properties.classes)
            return true
        case 'events':
            for (const e in properties.events)
                el.addEventListener(e, properties.events[e])
            return true
        case 'html':
            el.innerHTML = properties[p]
            return true
        case 'parent':
            properties.parent.appendChild(el)
            return true
        case 'parentFirst':
            properties.parentFirst.insertBefore(el, properties.parentFirst.firstChild)
            return true
        case 'styles':
            dom.set_styles(el, properties.styles)
            return true
        case 'text':
            dom.append_text(el, properties.text)
            return true
        }
        return false
    },
    set_html(el, html) {
        el = dom.$(el)
        let x = dom.extract_js(html)
        el.innerHTML = x.html
        if (x.js)
            dom.add_script(x.js)
        return el
    },
    set_style(el, style, value) {
        el = dom.$(el)
        if (typeof value != 'string') {
            if (dom.Styles[style] && value) {
                var map = dom.Styles[style].split(' ')
                value = (Array.isArray(value) ? value : [value]).map(function(val, i) {
                    if (!map[i]) return ''
                    return typeof val  == 'number' ? map[i].replace('@', val) : val
                }).join(' ')
            }
        }
        el.style[style] = value
        return el
    },
    set_styles(el, styles) {
        el = dom.$(el)
        for (const style in styles)
            dom.set_style(el, style, styles[style])
        return el
    },
    size(el) {
        if (el === window)
            return {x: window.innerWidth, y: window.innerHeight}
        if (el === document)
            el = document.body
        else
            el = dom.$(el)
        let r = el.getBoundingClientRect()
        return {x: r.width, y: r.height}
    },
    Styles: {
        left: '@px', top: '@px', bottom: '@px', right: '@px',
        width: '@px', height: '@px', maxWidth: '@px', maxHeight: '@px', minWidth: '@px', minHeight: '@px',
        backgroundColor: 'rgb(@, @, @)', backgroundSize: '@px', backgroundPosition: '@px @px', color: 'rgb(@, @, @)',
        fontSize: '@px', letterSpacing: '@px', lineHeight: '@px', clip: 'rect(@px @px @px @px)',
        margin: '@px @px @px @px', marginBottom: '@px', marginLeft: '@px', marginRight: '@px', marginTop: '@px',
        padding: '@px @px @px @px', paddingBottom: '@px', paddingLeft: '@px', paddingRight: '@px', paddingTop: '@px',
        border: '@px @ rgb(@, @, @) @px @ rgb(@, @, @) @px @ rgb(@, @, @)',
        borderWidth: '@px @px @px @px', borderStyle: '@ @ @ @', borderColor: 'rgb(@, @, @) rgb(@, @, @) rgb(@, @, @) rgb(@, @, @)',
        textIndent: '@px', borderRadius: '@px @px @px @px'
    },
    svg(properties) {
        var el = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
        for (const p in properties)
            if (!dom.set(el, p, properties))
                el.setAttribute(p, properties[p])
        return el;
    }
}

class ReminderWhen {
  constructor(id, num) {
    this.num = dom.$(id);
    this.num.addEventListener('change', this.on_num_change.bind(this));
    this.unit = this.num.nextElementSibling;
    this.unit.addEventListener('change', this.on_unit_change.bind(this));
    this.before = this.unit.nextElementSibling;
    this.before.addEventListener('change', this.on_before_change.bind(this));
    this.on_before_change();
    this.on_unit_change(num);
  }

  add_nums(from, to, mult, val) {
    for (let i = from; i <= to; i++) {
      let v = i * mult;
      this.num.appendChild(new Option(v, v, null, v == val));
    }
  }

  on_before_change() {
    let on_the_day = this.before.selectedIndex === 0;
    this.num.style.display = this.unit.style.display = on_the_day ? 'none' : 'inline';
    this.num.name = on_the_day ? '' : "num";
    this.unit.name = on_the_day ? '' : "unit";
    if (!on_the_day)
      this.on_num_change();
  }

  on_num_change() {
    if (this.num.options.length === 0)
            return;
    let one = this.num.options[this.num.selectedIndex].value === "1";
    for (let i = 0; i < this.unit.options.length; i++) {
      let text = this.unit.options[i].text;
      if (one) {
        if (text.endsWith('s'))
          this.unit.options[i].text = text.substring(0, text.length - 1);
      } else
      if (!text.endsWith('s'))
        this.unit.options[i].text = text + 's';
    }
  }

  on_unit_change(event) {
    let val = typeof event === 'object' ? this.num.options[this.num.selectedIndex].text : event;
    this.num.options.length = 0;
    let unit = this.unit.options[this.unit.selectedIndex].value;
    if (unit === 'day' || unit === 'week')
      this.add_nums(1, 10, 1, val);
    else if (unit === 'hour')
      this.add_nums(1, 23, 1, val);
    else
      this.add_nums(0, 3, 30, val);
    this.on_num_change();
  }
}

function create_reminder() {
  let form = document.createElement("form");
  form.setAttribute("method", "post");
  form.setAttribute("action", "");
  form.setAttribute("id", "reminder_form");

  // When
  let reminder_status_label = document.createElement("label");
  reminder_status_label.setAttribute("for", "reminder_status");
  reminder_status_label.innerHTML = "When";

  let reminder_status = document.createElement("select");
  reminder_status.setAttribute("name", "reminder_status");
  reminder_status.setAttribute("id", "reminder-status-sel");

  let reminder_status_1 = document.createElement("option");
  reminder_status_1.setAttribute("value", "on-the-day");
  reminder_status_1.setAttribute("id", "on-the-day");
  reminder_status_1.innerHTML = "On the day";

  let reminder_status_2 = document.createElement("option");
  reminder_status_2.setAttribute("value", "before");
  reminder_status_2.setAttribute("id", "before");
  reminder_status_2.innerHTML = "Before";

  // Note
  let note_label = document.createElement("label");
  note_label.setAttribute("for", "note");
  note_label.innerHTML = "Note";

  let note = document.createElement("textarea");
  note.setAttribute("name", "note");
  note.setAttribute("id", "reminder-note");
  note.setAttribute("row", "10");
  note.setAttribute("col", "60");

  // Send to
  let send_to_label = document.createElement("label");
  send_to_label.setAttribute("for", "send-to");
  send_to_label.innerHTML = "Send to Guest";

  let send_to = document.createElement("select");
  send_to.setAttribute("name", "send-to");

  let blank = document.createElement("option");
  blank.setAttribute("value", "--");

  let email_1 = document.createElement("option");
  email_1.setAttribute("value", "[email protected]");
  email_1.innerHTML = "[email protected]";

  let email_2 = document.createElement("option");
  email_2.setAttribute("value", "[email protected]");
  email_2.innerHTML = "[email protected]";

  let email_3 = document.createElement("option");
  email_3.setAttribute("value", "[email protected]");
  email_3.innerHTML = "[email protected]";

  let email_4 = document.createElement("option");
  email_4.setAttribute("value", "[email protected]");
  email_4.innerHTML = "[email protected]";

  let email_5 = document.createElement("option");
  email_5.setAttribute("value", "[email protected]");
  email_5.innerHTML = "[email protected]";

  let add_reminder_btn = document.createElement("button");
  add_reminder_btn.setAttribute("type", "button");
  add_reminder_btn.setAttribute("name", "add_reminder");
  add_reminder_btn.setAttribute("id", "add-reminder-to-list-btn");
  add_reminder_btn.innerHTML = "Add";

  let cancel_reminder_btn = document.createElement("button");
  cancel_reminder_btn.setAttribute("type", "button");
  cancel_reminder_btn.setAttribute("name", "cancel_reminder");
  cancel_reminder_btn.setAttribute("id", "cancel-reminder-to-list-btn");
  cancel_reminder_btn.innerHTML = "Cancel";
  cancel_reminder_btn.addEventListener('click', () => {
    form.style.visibility = 'hidden';
  })

  // Form
  form.appendChild(br.cloneNode());
  form.appendChild(reminder_status_label);
  form.appendChild(br.cloneNode());
  form.appendChild(reminder_status);
  reminder_status.appendChild(reminder_status_1);
  reminder_status.appendChild(reminder_status_2);
  form.appendChild(br.cloneNode());
  form.appendChild(br.cloneNode());

  form.appendChild(note_label);
  form.appendChild(br.cloneNode());
  form.appendChild(note);
  form.appendChild(br.cloneNode());
  form.appendChild(br.cloneNode());

  form.appendChild(send_to_label);
  form.appendChild(br.cloneNode());
  form.appendChild(send_to);
  send_to.appendChild(blank);
  send_to.appendChild(email_1);
  send_to.appendChild(email_2);
  send_to.appendChild(email_3);
  send_to.appendChild(email_4);
  send_to.appendChild(email_5);
  form.appendChild(br.cloneNode());

  form.appendChild(add_reminder_btn);
  form.appendChild(cancel_reminder_btn);

  document.getElementById("reminder")
    .appendChild(form);

let reminder_status_sel_txt = new ReminderWhen("reminder-status-sel", 2);
}

function list_reminder() {
  let add_reminder_btn = document.createElement("button");
  add_reminder_btn.setAttribute("type", "button");
  add_reminder_btn.setAttribute("name", "add_reminder");
  add_reminder_btn.setAttribute("class", "add-reminder-btn");
  add_reminder_btn.setAttribute("id", "add-reminder-btn");
  add_reminder_btn.innerHTML = "Add";
  add_reminder_btn.addEventListener('click', () => {
    create_reminder();
  })

  let reminder_table = document.createElement("table");
  reminder_table.setAttribute("id", "reminder-table");
  let reminder_table_head = document.createElement("thead");
  reminder_table_head.setAttribute("id", "reminder-list-header");
  let reminder_table_col_1 = document.createElement("th");
  reminder_table_col_1.innerHTML = "Send to";
  let reminder_table_col_2 = document.createElement("th");
  reminder_table_col_2.innerHTML = "When";
  let reminder_table_col_3 = document.createElement("th");

  let reminder_table_body = document.createElement("tbody");
  let reminder_table_row = document.createElement("tr");
  let reminder_table_cell_1 = document.createElement("td");
  reminder_table_cell_1.setAttribute("class", "reminder-list");
  let reminder_table_cell_2 = document.createElement("td");
  reminder_table_cell_2.setAttribute("class", "reminder-list");
  reminder_table_cell_2.setAttribute("id", "reminder-when");
  let reminder_table_cell_3 = document.createElement("td");
  reminder_table_cell_3.setAttribute("class", "reminder-list");

  reminder_table_cell_1.innerHTML = "[email protected]";

  let edit_reminder_btn = document.createElement("button");
  edit_reminder_btn.setAttribute("type", "button");
  edit_reminder_btn.setAttribute("name", "edit_reminder");
  edit_reminder_btn.setAttribute("class", "edit-delete-reminder-btns");
  edit_reminder_btn.innerHTML = "Edit";

  let delete_reminder_btn = document.createElement("button");
  delete_reminder_btn.setAttribute("type", "button");
  delete_reminder_btn.setAttribute("name", "delete_reminder");
  delete_reminder_btn.setAttribute("class", "edit-delete-reminder-btns");
  delete_reminder_btn.innerHTML = "Delete";

  reminder_table.appendChild(reminder_table_head);
  reminder_table_head.appendChild(reminder_table_col_1);
  reminder_table_head.appendChild(reminder_table_col_2);
  reminder_table_head.appendChild(reminder_table_col_3);

  reminder_table.appendChild(reminder_table_body);
  reminder_table_body.appendChild(reminder_table_row);
  reminder_table_row.appendChild(reminder_table_cell_1);
  reminder_table_row.appendChild(reminder_table_cell_2);
  reminder_table_row.appendChild(reminder_table_cell_3);
  reminder_table_cell_3.appendChild(edit_reminder_btn);
  reminder_table_cell_3.appendChild(delete_reminder_btn);

  document.getElementById("reminder")
    .appendChild(add_reminder_btn);

  document.getElementById("reminder")
    .appendChild(reminder_table);
}

Upvotes: 1

Views: 63

Answers (1)

Paulo Fernando
Paulo Fernando

Reputation: 3660

This code is really big and complex and doesn't seem to be doing much funcionality, I would suggest for you to study some modern javascript framework like React, even old JQuery would be good.

I tried to run your code and I got the same error Cannot read properties of undefined

this.unit receives in the constructor the next element after the <select name="num">, which is a <br> element, a br element doesn't have options, that's why you are getting this error

on_num_change() {
    if (this.num.options.length === 0) return;
    let one = this.num.options[this.num.selectedIndex].value === '1';
    for (let i = 0; i < this.unit.options.length; i++) { //unit.options is undefined, so it throws the error when trying to access .length
        let text = this.unit.options[i].text;
        if (one) {
            if (text.endsWith('s')) this.unit.options[i].text = text.substring(0, text.length - 1);
        } else if (!text.endsWith('s')) this.unit.options[i].text = text + 's';
    }
}

Upvotes: 1

Related Questions