Reputation: 57
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. 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
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