Local Storage with multiple contentEditable - javascript

I have currently figured out a way to store value of 6am plan in a content editable box in my local storage key
item 2
How do I make this happen for all the other hours like 1
work plan for every hour of the day 6 am - 11pm
storing input in one key
using this code snippet below
javascript -
var content = document.getElementById('content'),
address = document.getElementById('address'),
saveButton = document.getElementById('save'),
loadButton = document.getElementById('load'),
clearButton = document.getElementById('clear'),
resetButton = document.getElementById('reset');
var localStore = {
saveLocalStorage: function() {
localStorage.setItem('item', content.innerHTML);
},
loadLocalStorage: function() {
var contentStored = localStorage.getItem('item');
if ( contentStored ) {
content.innerHTML = contentStored;
}
},
clearLocalStorage: function() {
localStorage.removeItem('item');
}
};
saveButton.addEventListener('click', function() {
localStore.saveLocalStorage();
}, false);
<r class="notion-table-row">
<td
style="color: inherit; fill: inherit; border: 1px solid gb(233, 233, 231); position: relative; vertical-align: top; min-width: 122px; max-width: 122px; min-height: 32px;">
<div class="notion-table-cell">
<div class="notion-table-cell-text"
spellcheck="true" placeholder=" "
data-content-editable-leaf="true"
style="max-width: 100%; width: 100%; white-space: pre-wrap; word-break: break-word; caret-colour: gb(55, 53, 47); padding: 7px 9px; background-colour: transparent; font-size: 14px; line-height: 20px;"
content editable="false">11 PM</div>
</div>
</td>
<td
style="color: inherit; fill: inherit; border: 1px solid gb(233, 233, 231); position: relative; vertical-align: top; min-width: 200px; max-width: 200px; min-height: 32px;">
<div class="notion-table-cell">
<div class="notion-table-cell-text"
spellcheck="true" placeholder=" "
data-content-editable-leaf="true"
style="max-width: 100%; width: 100%; white-space: pre-wrap; word-break: break-word; caret-colour: gb (55, 53, 47); padding: 7px 9px; background - colour: transparent; font-size: 14px; line-height: 20px;"
<section id="11pm_input" content editable="true"></div>

Store all of the data in an array. Keep in mind, localStorage stores only strings so anything not a string (ex. array, object, number, etc.), must be converted into a string when saved to localStorage:
localStorage.setItem("String", JSON.stringify([...Array]))
and when it is retrieved from localStorge it needs to be parsed into it's original type:
const data = JSON.parse(localStorage.getItem("String"));
In the example below, the data is gathered from all .cell which comprise of the following HTML elements from the table head and body (in ex. 4 x time, 6 x data):
<time class='cell' datetime="23:00">11 PM</time>
<data class='cell' value="0ne Zer0">Zer0 0ne</data>
time.datetime = "23:00";
time.textContent = "11 PM";
data.value = "0ne Zer0";
data.textContent = "Zer0 0ne";
lStorage = [
["23:00", "11 PM"],
["0ne Zer0", "Zer0 0ne"],
["00:00", "12 AM"],
...[N, N]
];
The event handler manageData(event) delegated all click events triggered on the table head, body, and foot.
Variables:
key is whatever the user typed in .input, the value will be assigned to data being saved to localStorage.
data is declared for data coming from and to localStorage.
cells is an array of all time.cell and data.cell tags within the table head and body.
node is determined by event.target property which always refers to the tag the user actually clicked. This reference will be delegated to react according to the .matches() method and a series of flow control statements (if, if else, and else).
The process for loading data from localStorage involves loadData(key) and iteration of the array of arrays saved in localStorage and the array of .cells:
if (node.matches('.load')) {...
...
data = loadData(key);
cells.forEach((n, i) => {
n.textContent = data[i][1];
if (n.matches('time')) {
n.datetime = data[i][0];
} else {
n.value = data[i][0];
}
});
The rest of the code is of simular fashion -- here's a brief description of what it can do:
Load data from key in localStorage and popualte a <table> with it.
Save data from a <table> to a key in localStorage.
Reset data the user typed in the fields (if given a selector, it can reset it as well).
Clear data by key and all data in <table> (including static data).
All .cells are editable (including headers). Double click any .cell within the table head or body to toggle it in/out of edit mode.
Due to SO security policy, WebStorage API will not function, go to: Plunker
The link doesn't work for me when clicked but
I managed to use the url by copy & paste
to the address bar:
https://run.plnkr.co/preview/ckz3pfkfe0007386nlancnzqk/
If the links above don't work, you can copy & paste the code in the Snippet with a text editor and save the file with a *.html extension.
<!DOCTYPE html>
<html lang="en">
<head>
<title></title>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<style>
:root {
font: 1ch/1.5 'Segoe UI';
}
body {
font-size: 2ch;
}
table {
table-layout: fixed;
width: 70vw;
margin: 20px auto;
}
th {
width: 50%;
font-size: 2.25rem;
}
td {
vertical-align: center;
}
.box {
display: flex;
flex-flow: column nowrap;
justify-content: center;
align-items: center;
min-height: 32px;
padding: 8px 8px 5px;
}
.cell {
display: block;
max-width: 100%;
width: 100%;
min-height: 25px;
white-space: pre-wrap;
word-break: break-word;
font-size: 2rem;
border: 2px solid #000;
background: transparent;
text-align: center;
}
.head {
width: 97.5%;
border-color: transparent;
}
.edit {
border-color: blue;
}
button {
display: block;
font: inherit;
font-size: 2rem;
background: transparent;
border-radius: 6px;
cursor: pointer;
}
button:hover {
border-color: blue;
color: blue;
background: #ddd;
}
.ctrl {
position: relative;
flex-flow: row nowrap;
justify-content: space-between;
min-width: 92%;
height: 30px;
margin-top: 40px;
}
.input {
position: absolute;
top: -40px;
left: -0.5vw;
width: 99%;
height: 25px;
}
.noKey {
color: tomato;
font-weight: 900;
border-color: tomato;
}
.noKey::before {
content: 'Enter the key to data';
}
.done {
color: blue;
border-color: blue;
}
.done::before {
content: 'Data is saved under key: "';
}
.done::after {
content: '"';
}
</style>
</head>
<body>
<main>
<section>
<table>
<thead>
<tr>
<th>
<data class="cell head" value="Hour">Hour</data>
</th>
<th>
<data class="cell head" value="Employee">Employee</data>
</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<fieldset class="box">
<time class="cell">8 PM</time>
</fieldset>
</td>
<td>
<fieldset class="box">
<data class="cell edit" contenteditable></data>
</fieldset>
</td>
</tr>
<tr>
<td>
<fieldset class="box">
<time class="cell">9 PM</time>
</fieldset>
</td>
<td>
<fieldset class="box">
<data class="cell edit" contenteditable></data>
</fieldset>
</td>
</tr>
<tr>
<td>
<fieldset class="box">
<time class="cell">10 PM</time>
</fieldset>
</td>
<td>
<fieldset class="box">
<data class="cell edit" contenteditable></data>
</fieldset>
</td>
</tr>
<tr>
<td>
<fieldset class="box">
<time class="cell">11 PM</time>
</fieldset>
</td>
<td>
<fieldset class="box">
<data class="cell edit" contenteditable></data>
</fieldset>
</td>
</tr>
</tbody>
<tfoot>
<tr>
<td colspan="2">
<fieldset class="ctrl box">
<data class="cell input edit" contenteditable></data>
<button class="load" title="Load data">
Load</button>
<button class="save" title="Save data">
Save</button>
<button class="reset" title="Reset fields">
Reset</button>
<button class="clear" title="Clear saved data and Reset fields">
Clear</button>
</fieldset>
</td>
</tr>
</tfoot>
</table>
</section>
</main>
<script>
const tab = document.querySelector('table');
const hdr = tab.tHead;
const mid = tab.tBodies[0];
const ftr = tab.tFoot;
const cls = ['noKey', 'done'];
const inp = document.querySelector('.input');
const formatTime = time => {
const T = time.split(' ');
return T[1] === 'PM' ? `${+T[0]+12}:00` : `${T[0]}:00`;
};
const lastFirst = name => name.split(' ').reverse().join(', ');
const loadData = key => JSON.parse(localStorage.getItem(key));
const saveData = (key, data) => localStorage.setItem(key, JSON.stringify(data));
const resetData = selector => {
let nodes = selector === undefined ? '.edit' : selector;
[...document.querySelectorAll(nodes)].forEach(n => {
n.textContent = '';
if (n.matches('time')) {
n.datetime = '';
} else {
n.value = '';
}
});
inp.classList.remove(...cls);
};
const clearData = key => {
resetData('.cell');
inp.classList.remove(...cls);
localStorage.removeItem(key);
};
const manageData = e => {
let key = inp.textContent;
let data;
const cells = [...document.querySelectorAll('.cell')];
const node = e.target;
if (node.matches('.load')) {
if (key.length < 1) {
inp.classList.add('noKey');
} else {
data = loadData(key);
cells.forEach((n, i) => {
n.textContent = data[i][1];
if (n.matches('time')) {
n.datetime = data[i][0];
} else {
n.value = data[i][0];
}
});
}
} else if (node.matches('.save')) {
if (key.length < 1) {
inp.classList.add('noKey');
} else {
data = cells.flatMap(n => {
if (n.matches('time')) {
n.datetime = formatTime(n.textContent);
return [
[n.datetime, n.textContent]
];
} else {
n.value = lastFirst(n.textContent);
return [
[n.value, n.textContent]
];
}
});
inp.classList.add('done');
saveData(key, data);
}
} else if (node.matches('.reset')) {
resetData();
} else if (node.matches('.clear')) {
if (key.length < 1) {
inp.classList.add('noKey');
} else {
clearData(key);
}
} else if (node.matches('.input')) {
node.textContent = '';
node.classList.remove(...cls);
} else {
return;
}
};
ftr.onclick = manageData;
const toggleEdit = e => {
const node = e.target;
if (node.matches('.cell')) {
node.classList.toggle('edit');
node.toggleAttribute('contenteditable');
}
};
hdr.ondblclick = toggleEdit;
mid.ondblclick = toggleEdit;
</script>
</body>
</html>

Related

Display output with a string character and with an input?

I have two things that relate to adding a character to the output view.
Pronouns - Right now, it is hidden until someone types in their preferred pronoun. It'll output her/she if that's what they put, but I would like to do '(' + "her/she" + ')';
2.The output is hidden until someone types a number. I would like to have it as display M: 739.383.3893.
I can get the outputs to display the input text but never the with the character. How do I go about adding characters into the output based on the input the user puts in?
Extreme beginner here, I'm sorry :(
(function() {
/*
* Input stuff
*/
var doc = document;
var form = doc.getElementById('form');
var copyButton = doc.getElementById('copy');
var resetButton = doc.getElementById('reset');
var inputPhone = doc.getElementById('phone');
var inputOffice = doc.getElementById('office');
var instructions = doc.getElementById('instructions');
var inputFullName = doc.getElementById('fullName');
var inputPronouns = doc.getElementById('pronouns');
var inputJobTitle = doc.getElementById('jobTitle');
var copyButtonOriginalHTML = '<i class="fas fa-copy"></i> Copy Signature';
var copyButtonDisabledHTML = '<i class="fas fa-exclamation-circle"></i> Enter your info first!';
var peopleTemplate = {
empty: {
fullName: "",
pronouns: "",
jobTitle: "",
phone: "",
office: ""
},
dummy: {
fullName: "Your Name",
jobTitle: "Your title",
pronouns: "Your pronouns",
office: "7890",
phone: "123-456-7890"
}
};
/*
* Output stuff
*/
var sig = doc.getElementById('sig');
var sigPhone = doc.querySelector('.sig__phone');
var sigFullName = doc.querySelector('.sig__fullName');
var sigJobTitle = doc.querySelector('.sig__jobTitle');
var sigPronouns = doc.querySelector('.sig__pronouns');
var sigOffice = doc.querySelector('.sig__office');
/*
* Instructions HTML
*/
var pasteInstructions = "<h3>Yay! Your signature was copied and is ready to paste.</h3>"
+ "<p>To create a new signature in Outlook, follow these directions:</p>"
+ "<ol><li>Update Outlook to the latest version.</li>"
+ "<li>Open Outlook.</li>"
+ "<li>Under <b>Outlook</b> in the main menu, select <b>Preferences</b>.</li>"
+ "<li>Under the <b>Email</b> section, click <b>Signatures</b>.</li>"
+ "<li>In the <b>Edit Signatures</b> section, click the <b>+</b> (plus) icon in the bottom left corner.</li>"
+ "<li>Select whatever is there already and paste your new signature into the box.</li>"
+ "</ol>";
/*
* Clear form inputs
*/
var resetForm = function () {
inputFullName.value = '';
inputJobTitle.value = '';
inputPhone.value = '';
inputPronouns.value = '';
inputOffice.value = '';
updateSignature();
instructions.innerHTML = '';
};
/*
* Fill signature with dummy info
*/
var fillDummySignature = function () {
sigFullName.textContent = "Your Name";
sigPronouns.textContent = ""
sigJobTitle.textContent = "Your title";
sigPhone.textContent = "";
sigOffice.textContent = "1234";
};
/*
* Check if nothing is entered
*/
var inputsAreEmpty = function () {
return inputFullName.value === ''
&& inputPronouns.value === ''
&& inputJobTitle.value === ''
&& inputPhone.value === ''
&& inputOffice.value === '';
};
var userName = document.querySelector('#phone');
userName.addEventListener('input', restrictNumber);
function restrictNumber (e) {
var newValue = this.value.replace(new RegExp(/[^\d]/,'ig'), "");
this.value = newValue;
}
/*
* Reformat phone number syntax
*/
var formatPhone = function (n) {
// var pattern = /[^0-9.]+/g;
// if (n.search(pattern) !== -1) {
// console.log("not a number");
// // n.replace(pattern, '');
// return n;
// }
var o = n;
var l = n.length;
var noDash = function (value, index) {
return value.charAt(index) !== '.';
};
var insertDash = function (value, index) {
return value.slice(0, index) + '.' + value.slice(index, value.length + 1);
};
var no3 = noDash(o, 3);
var no7 = noDash(o, 7);
if (l > 3 && l <= 7) {
if (no3) {
o = insertDash(n, 3);
}
} else if (l > 7 && l <= 11) {
if (no3) {
o = insertDash(n, 3);
if (no7) {
o = insertDash(o, 7); // insert on the value we just updated
}
} else if (no7) {
o = insertDash(n, 7);
}
} else if (l > 12) {
o = n.slice(0, 12);
}
return o;
};
/*
* Add the input values into the actual signature
*/
var updateSignature = function (event) {
if (inputsAreEmpty()) {
fillDummySignature();
// Button states
copyButton.disabled = true;
copyButton.innerHTML = copyButtonDisabledHTML;
resetButton.style.display = 'none';
} else {
// Button states
copyButton.disabled = false;
copyButton.innerHTML = copyButtonOriginalHTML;
resetButton.style.display = 'inline-block';
// Populate signature fields
if (event && event.target.tagName === 'INPUT') {
var id = event.target.id;
var input = doc.getElementById(id);
var sigClassName = '.sig__' + id;
var inputIdName = '#' + id;
var sigTarget = doc.querySelector(sigClassName);
var inputTarget = doc.querySelector(inputIdName);
if (id === 'fullName') {
sigTarget.textContent = input.value;
} else if (id === 'phone') {
sigTarget.textContent = formatPhone(input.value);
inputTarget.value = formatPhone(input.value);
} else {
sigTarget.textContent = input.value;
}
} else {
sigFullName.textContent = inputFullName.value;
sigJobTitle.textContent = inputJobTitle.value;
sigPhone.textContent = inputPhone.value;
}
}
}
/*
* Insert a person's info when option is selected
*/
var insertPersonInfo = function (event) {
resetForm();
if (event.target.value !== 'custom') {
var person = people[this.selectedIndex - 1];
inputFullName.value = person.fullName;
inputPronouns.value = person.pronouns;
inputJobTitle.value = person.jobTitle;
inputPhone.value = person.phone;
updateSignature(event);
}
};
/*
* Populate the people info in the select menu on load
*/
document.addEventListener("DOMContentLoaded", function (event) {
updateSignature(event);
fillDummySignature();
});
/*
* Copy raw HTML output
*/
copyButton.addEventListener('click', function(event) {
// Have to remove any existing ranges :: Chrome bug
window.getSelection().removeAllRanges();
// Create range and add it to selection
var r = document.createRange();
r.selectNode(sig);
window.getSelection().addRange(r);
// Error catching
try {
var successful = document.execCommand('copy');
var msg = successful ? 'successful' : 'unsuccessful';
console.log('Copy command was ' + msg);
} catch(err) {
console.log('Oops, unable to copy');
}
// Remove range from selection again
window.getSelection().removeAllRanges();
if (successful) {
instructions.innerHTML = pasteInstructions;
// this.parentNode.removeChild(this);
}
});
/*
* Listeners
*/
form.addEventListener('input', updateSignature);
resetButton.addEventListener('click', resetForm);
inputPhone.addEventListener('paste', function(event) {
// formatPhone();
});
}());
.form__input, .button, .button--copy, .button--reset {
font-size: 14px;
margin: 0;
padding: 6px 9px;
border: 1px solid #e7e7e7;
border-radius: 2px;
line-height: 1;
}
* {
box-sizing: border-box;
}
.sig-gen {
font-family: 'Work Sans', 'Helvetica Neue', Arial, sans-serif;
font-size: 16px;
margin: 2em auto 4em;
width: 100%;
max-width: 800px;
}
.sig-gen__section {
margin-bottom: 2em;
}
.sig-gen__section--email {
margin-bottom: 3em;
}
.sig__field, .set-inform, .links-text {
font-family: 'Work Sans', 'Helvetica Neue', Arial, sans-serif;
}
.form {
display: flex;
justify-content: space-between;
flex-direction: column;
}
.set-inform {
display: inline-block;
}
#media screen and (min-width: 600px) {
.form {
/* flex-direction: row; */
}
}
.form__group {
margin-bottom: 12px;
}
.form__group:last-of-type {
margin-bottom: 0;
}
#media screen and (min-width: 600px) {
.form__group {
margin-bottom: 10px;
}
}
.form label {
display: block;
margin-bottom: 6px;
}
.form__input {
background: white;
width: 100%;
}
.form__input:focus, .form__input:active {
outline: 0;
border-color: #bebebe;
}
.email_generator {
position: relative;
border: 1px solid #e7e7e7;
border-top: none;
border-bottom: none;
border-bottom-left-radius: 6px;
border-bottom-right-radius: 6px;
box-shadow: 0 6px 0 #ccc;
}
.email_generator:before {
content: "";
position: absolute;
top: 0;
left: -1px;
width: calc(100% + 2 * 1px);
height: 28%;
background: linear-gradient(white, rgba(255, 255, 255, 0));
}
.email__container {
padding: 28px;
}
.email__sig {
margin-top: 51px;
}
.email__line {
height: 12px;
margin-bottom: 12px;
background: #e7e7e7;
border-radius: 1px;
}
.email__line:last-child {
width: 66%;
margin-bottom: 28px;
}
.email__signoff .email__line {
width: 17%;
}
.sig__field {
font-size: 14px;
}
.sig__fullName {
font-size: 18px;
}
.button, .button--copy, .button--reset {
padding: 9px 12px;
color: white;
cursor: pointer;
background: #8c4049;
border-color: #823b44;
}
.button:hover, .button--copy:hover, .button--reset:hover {
background: #97454e;
border-color: #8c4049;
}
.button:focus, .button--copy:focus, .button--reset:focus, .button:active, .button--copy:active, .button--reset:active {
outline: 0;
background: #77363e;
border-color: #622d33;
}
.button:disabled, .button--copy:disabled, .button--reset:disabled {
background: #656669;
border-color: #5d5e61;
cursor: not-allowed;
color: white;
}
.button--reset {
background: #e2e3e4;
border-color: #dadbdd;
color: #656669;
}
.button--reset:hover {
background: #eaebeb;
border-color: #e2e3e4;
}
.button--reset:focus, .button--reset:active {
outline: 0;
background: #d2d4d5;
border-color: #c2c4c6;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<div id="app" class="sig-gen">
<section class="sig-gen__section">
<h2>Email Signature Generator</h2>
</section>
<section class="sig-gen__section">
<form id="form" class="form">
<div class="form__group">
<label for="fullName">Full Name:</label>
<input type="text" id="fullName" class="form__input" placeholder="Your name" value="">
</div>
<div class="form__group">
<label for="pronouns">Pronouns:</label>
<input type="text" id="pronouns" class="form__input" placeholder="optional (they/them)" value="">
</div>
<div class="form__group">
<label for="jobTitle">Job Title:</label>
<input type="text" id="jobTitle" class="form__input" placeholder="Your title" value="">
</div>
<div class="form__group">
<label for="office">Office Extension:</label>
<input type="text" id="office" class="form__input" pattern="[0-9]" maxlength="4" placeholder="1234" value="">
</div>
<div class="form__group">
<label for="phone">Mobile:</label>
<input type="text" id="phone" class="form__input" pattern="[0-9]" maxlength="12" placeholder="optional" value="">
</div>
</form>
</section>
<section class="sig-gen__section sig-gen__section--email">
<div class="email_generator">
<div class="email__container">
<div id="sig" class="email__sig">
<table border="0" cellpadding="0" cellspacing="0" style="border-collapse:collapse;mso-table-lspace:0pt;mso-table-rspace:0pt;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%;font-family:Helvetica,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:normal;color:#313030;line-height:1.5;">
<tbody border="0" cellpadding="0" cellspacing="0">
<tr border="0" cellpadding="0" cellspacing="0">
<td align="left" height="28" style="mso-table-lspace:0pt;mso-table-rspace:0pt;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%;font-family:Arial,sans-serif;font-size:16px;font-weight:normal;color:#313030;line-height:1.5;" valign="top">
<div>
<strong class="sig__field sig__fullName set-inform" style="font-size: 16px; color:#002857;"></strong><div class="sig__field sig__pronouns set-inform" style="font-size: 13px; color: #002857; font-style: italic;"></div>
</div>
<div class="sig__field sig__jobTitle" style="font-size: 15px; color: #7f7f7f"></div>
<div class="links-text" style="font-size: 15px; color: #7f7f7f">3847 New York, New York</div>
<div class="set-inform" style="font-size: 15px; color: #7f7f7f">O: 383.384.4838 ext.</div><div class="sig__field sig__office set-inform" style="font-size: 15px; color: #7f7f7f"></div><span> </span><div class="sig__phone set-inform" style="font-size: 15px; color: #7f7f7f"></div>
<div class="links-text" style="font-size: 15px;"><a style="color: #7f7f7f;" href="#">FB</a> | <a style="color: #7f7f7f;" href="#">Twitter</a> | <a style="color: #7f7f7f;" href="#">Instagram</a> | <a style="color: #7f7f7f;" href="#">LinkedIn</a></div>
</td>
</tr>
<tr border="0" cellpadding="0" cellspacing="0">
<td align="center" style="mso-table-lspace:0pt;mso-table-rspace:0pt;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%;font-family:Arial,sans-serif;font-size:16px;font-weight:normal;color:#313030;line-height:1.5;margin:0;padding:0;" valign="top">
<table border="0" cellpadding="0" cellspacing="0" style="border-collapse:collapse;mso-table-lspace:0pt;mso-table-rspace:0pt;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%;font-family:Helvetica,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:normal;color:#313030;line-height:1.5;">
<tbody border="0" cellpadding="0" cellspacing="0">
<tr border="0" cellpadding="0" cellspacing="0">
<td align="left" style="mso-table-lspace:0pt;mso-table-rspace:0pt;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%;line-height:1.4;" valign="top">
New York University<span style="margin-left:10px;margin-right: 10px;border-left: solid; border-color: #002857;border-width: 2px;"></span><span style="font-weight: bold;color: #C20F2F;font-size:18px;letter-spacing: 1px;"> NYU</span>
</td>
</tr>
<tr border="0" cellpadding="0" cellspacing="0">
<td align="left" height="16" style="mso-table-lspace:0pt;mso-table-rspace:0pt;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%;" valign="top">
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</section>
<section class="sig-gen__section">
<button id="copy" class="button--copy"></button>
<button id="reset" class="button--reset"><i class="fas fa-sync"></i> Reset Form</button>
</section>
<section class="sig-gen__section">
<div id="instructions"></div>
</section>
</div>
[x] displaying of (pronoun) is done,
[x] prepending M. to the displayed mobile number is done,
(function() {
/*
* Input stuff
*/
var doc = document;
var form = doc.getElementById('form');
var copyButton = doc.getElementById('copy');
var resetButton = doc.getElementById('reset');
var inputPhone = doc.getElementById('phone');
var inputOffice = doc.getElementById('office');
var instructions = doc.getElementById('instructions');
var inputFullName = doc.getElementById('fullName');
var inputPronouns = doc.getElementById('pronouns');
var inputJobTitle = doc.getElementById('jobTitle');
var copyButtonOriginalHTML = '<i class="fas fa-copy"></i> Copy Signature';
var copyButtonDisabledHTML = '<i class="fas fa-exclamation-circle"></i> Enter your info first!';
var peopleTemplate = {
empty: {
fullName: "",
pronouns: "",
jobTitle: "",
phone: "",
office: ""
},
dummy: {
fullName: "Your Name",
jobTitle: "Your title",
pronouns: "Your pronouns",
office: "7890",
phone: "123-456-7890"
}
};
/*
* Output stuff
*/
var sig = doc.getElementById('sig');
var sigPhone = doc.querySelector('.sig__phone');
var sigFullName = doc.querySelector('.sig__fullName');
var sigJobTitle = doc.querySelector('.sig__jobTitle');
var sigPronouns = doc.querySelector('.sig__pronouns');
var sigOffice = doc.querySelector('.sig__office');
/*
* Instructions HTML
*/
var pasteInstructions = "<h3>Yay! Your signature was copied and is ready to paste.</h3>" +
"<p>To create a new signature in Outlook, follow these directions:</p>" +
"<ol><li>Update Outlook to the latest version.</li>" +
"<li>Open Outlook.</li>" +
"<li>Under <b>Outlook</b> in the main menu, select <b>Preferences</b>.</li>" +
"<li>Under the <b>Email</b> section, click <b>Signatures</b>.</li>" +
"<li>In the <b>Edit Signatures</b> section, click the <b>+</b> (plus) icon in the bottom left corner.</li>" +
"<li>Select whatever is there already and paste your new signature into the box.</li>" +
"</ol>";
/*
* Clear form inputs
*/
var resetForm = function() {
inputFullName.value = '';
inputJobTitle.value = '';
inputPhone.value = '';
inputPronouns.value = '';
inputOffice.value = '';
updateSignature();
instructions.innerHTML = '';
};
/*
* Fill signature with dummy info
*/
var fillDummySignature = function() {
sigFullName.textContent = "Your Name";
sigPronouns.textContent = ""
sigJobTitle.textContent = "Your title";
sigPhone.textContent = "";
sigOffice.textContent = "1234";
};
/*
* Check if nothing is entered
*/
var inputsAreEmpty = function() {
return [inputFullName, inputPronouns, inputJobTitle, inputPhone, inputOffice].every(({
value
}) => value === '')
};
var userName = document.querySelector('#phone');
userName.addEventListener('input', restrictNumber);
function restrictNumber(e) {
var newValue = this.value.replace(new RegExp(/[^\d]/, 'ig'), "");
this.value = newValue;
}
/*
* Reformat phone number syntax
*/
var formatPhone = function(n) {
// var pattern = /[^0-9.]+/g;
// if (n.search(pattern) !== -1) {
// console.log("not a number");
// // n.replace(pattern, '');
// return n;
// }
var o = n;
var l = n.length;
var noDash = function(value, index) {
return value.charAt(index) !== '.';
};
var insertDash = function(value, index) {
return value.slice(0, index) + '.' + value.slice(index, value.length + 1);
};
var no3 = noDash(o, 3);
var no7 = noDash(o, 7);
if (l > 3 && l <= 7) {
if (no3) {
o = insertDash(n, 3);
}
} else if (l > 7 && l <= 11) {
if (no3) {
o = insertDash(n, 3);
if (no7) {
o = insertDash(o, 7); // insert on the value we just updated
}
} else if (no7) {
o = insertDash(n, 7);
}
} else if (l > 12) {
o = n.slice(0, 12);
}
return o;
};
/*
* Add the input values into the actual signature
*/
var updateSignature = function(event) {
if (inputsAreEmpty()) {
fillDummySignature();
// Button states
copyButton.disabled = true;
copyButton.innerHTML = copyButtonDisabledHTML;
resetButton.style.display = 'none';
} else {
// Button states
copyButton.disabled = false;
copyButton.innerHTML = copyButtonOriginalHTML;
resetButton.style.display = 'inline-block';
// Populate signature fields
if (event && event.target.tagName === 'INPUT') {
var id = event.target.id;
var input = doc.getElementById(id);
var sigClassName = '.sig__' + id;
var inputIdName = '#' + id;
var sigTarget = doc.querySelector(sigClassName);
var inputTarget = doc.querySelector(inputIdName);
if (id === 'fullName') {
sigTarget.textContent = input.value;
} else if (id === 'phone') {
// just save the value in a variable, and use that
const formattedPhone = formatPhone(input.value);
sigTarget.textContent = `M. ${formattedPhone}`;
inputTarget.value = formattedPhone
} else if (id === 'pronouns') {
// this case needed to be treated separately
sigTarget.textContent = `(${input.value})`
} else {
sigTarget.textContent = input.value;
}
} else {
sigFullName.textContent = inputFullName.value;
sigJobTitle.textContent = inputJobTitle.value;
sigPhone.textContent = inputPhone.value;
}
}
}
/*
* Insert a person's info when option is selected
*/
var insertPersonInfo = function(event) {
resetForm();
if (event.target.value !== 'custom') {
var person = people[this.selectedIndex - 1];
inputFullName.value = person.fullName;
inputPronouns.value = person.pronouns;
inputJobTitle.value = person.jobTitle;
inputPhone.value = person.phone;
updateSignature(event);
}
};
/*
* Populate the people info in the select menu on load
*/
document.addEventListener("DOMContentLoaded", function(event) {
updateSignature(event);
fillDummySignature();
});
/*
* Copy raw HTML output
*/
copyButton.addEventListener('click', function(event) {
// Have to remove any existing ranges :: Chrome bug
window.getSelection().removeAllRanges();
// Create range and add it to selection
var r = document.createRange();
r.selectNode(sig);
window.getSelection().addRange(r);
// Error catching
try {
var successful = document.execCommand('copy');
var msg = successful ? 'successful' : 'unsuccessful';
console.log('Copy command was ' + msg);
} catch (err) {
console.log('Oops, unable to copy');
}
// Remove range from selection again
window.getSelection().removeAllRanges();
if (successful) {
instructions.innerHTML = pasteInstructions;
// this.parentNode.removeChild(this);
}
});
/*
* Listeners
*/
form.addEventListener('input', updateSignature);
resetButton.addEventListener('click', resetForm);
inputPhone.addEventListener('paste', function(event) {
// formatPhone();
});
}());
.form__input,
.button,
.button--copy,
.button--reset {
font-size: 14px;
margin: 0;
padding: 6px 9px;
border: 1px solid #e7e7e7;
border-radius: 2px;
line-height: 1;
}
* {
box-sizing: border-box;
}
.sig-gen {
font-family: 'Work Sans', 'Helvetica Neue', Arial, sans-serif;
font-size: 16px;
margin: 2em auto 4em;
width: 100%;
max-width: 800px;
}
.sig-gen__section {
margin-bottom: 2em;
}
.sig-gen__section--email {
margin-bottom: 3em;
}
.sig__field,
.set-inform,
.links-text {
font-family: 'Work Sans', 'Helvetica Neue', Arial, sans-serif;
}
.form {
display: flex;
justify-content: space-between;
flex-direction: column;
}
.set-inform {
display: inline-block;
}
#media screen and (min-width: 600px) {
.form {
/* flex-direction: row; */
}
}
.form__group {
margin-bottom: 12px;
}
.form__group:last-of-type {
margin-bottom: 0;
}
#media screen and (min-width: 600px) {
.form__group {
margin-bottom: 10px;
}
}
.form label {
display: block;
margin-bottom: 6px;
}
.form__input {
background: white;
width: 100%;
}
.form__input:focus,
.form__input:active {
outline: 0;
border-color: #bebebe;
}
.email_generator {
position: relative;
border: 1px solid #e7e7e7;
border-top: none;
border-bottom: none;
border-bottom-left-radius: 6px;
border-bottom-right-radius: 6px;
box-shadow: 0 6px 0 #ccc;
}
.email_generator:before {
content: "";
position: absolute;
top: 0;
left: -1px;
width: calc(100% + 2 * 1px);
height: 28%;
background: linear-gradient(white, rgba(255, 255, 255, 0));
}
.email__container {
padding: 28px;
}
.email__sig {
margin-top: 51px;
}
.email__line {
height: 12px;
margin-bottom: 12px;
background: #e7e7e7;
border-radius: 1px;
}
.email__line:last-child {
width: 66%;
margin-bottom: 28px;
}
.email__signoff .email__line {
width: 17%;
}
.sig__field {
font-size: 14px;
}
.sig__fullName {
font-size: 18px;
}
.button,
.button--copy,
.button--reset {
padding: 9px 12px;
color: white;
cursor: pointer;
background: #8c4049;
border-color: #823b44;
}
.button:hover,
.button--copy:hover,
.button--reset:hover {
background: #97454e;
border-color: #8c4049;
}
.button:focus,
.button--copy:focus,
.button--reset:focus,
.button:active,
.button--copy:active,
.button--reset:active {
outline: 0;
background: #77363e;
border-color: #622d33;
}
.button:disabled,
.button--copy:disabled,
.button--reset:disabled {
background: #656669;
border-color: #5d5e61;
cursor: not-allowed;
color: white;
}
.button--reset {
background: #e2e3e4;
border-color: #dadbdd;
color: #656669;
}
.button--reset:hover {
background: #eaebeb;
border-color: #e2e3e4;
}
.button--reset:focus,
.button--reset:active {
outline: 0;
background: #d2d4d5;
border-color: #c2c4c6;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<div id="app" class="sig-gen">
<section class="sig-gen__section">
<h2>Email Signature Generator</h2>
</section>
<section class="sig-gen__section">
<form id="form" class="form">
<div class="form__group">
<label for="fullName">Full Name:</label>
<input type="text" id="fullName" class="form__input" placeholder="Your name" value="">
</div>
<div class="form__group">
<label for="pronouns">Pronouns:</label>
<input type="text" id="pronouns" class="form__input" placeholder="optional (they/them)" value="">
</div>
<div class="form__group">
<label for="jobTitle">Job Title:</label>
<input type="text" id="jobTitle" class="form__input" placeholder="Your title" value="">
</div>
<div class="form__group">
<label for="office">Office Extension:</label>
<input type="text" id="office" class="form__input" pattern="[0-9]" maxlength="4" placeholder="1234" value="">
</div>
<div class="form__group">
<label for="phone">Mobile:</label>
<input type="text" id="phone" class="form__input" pattern="[0-9]" maxlength="12" placeholder="optional" value="">
</div>
</form>
</section>
<section class="sig-gen__section sig-gen__section--email">
<div class="email_generator">
<div class="email__container">
<div id="sig" class="email__sig">
<table border="0" cellpadding="0" cellspacing="0" style="border-collapse:collapse;mso-table-lspace:0pt;mso-table-rspace:0pt;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%;font-family:Helvetica,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:normal;color:#313030;line-height:1.5;">
<tbody border="0" cellpadding="0" cellspacing="0">
<tr border="0" cellpadding="0" cellspacing="0">
<td align="left" height="28" style="mso-table-lspace:0pt;mso-table-rspace:0pt;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%;font-family:Arial,sans-serif;font-size:16px;font-weight:normal;color:#313030;line-height:1.5;" valign="top">
<div>
<strong class="sig__field sig__fullName set-inform" style="font-size: 16px; color:#002857;"></strong>
<div class="sig__field sig__pronouns set-inform" style="font-size: 13px; color: #002857; font-style: italic;"></div>
</div>
<div class="sig__field sig__jobTitle" style="font-size: 15px; color: #7f7f7f"></div>
<div class="links-text" style="font-size: 15px; color: #7f7f7f">3847 New York, New York</div>
<div class="set-inform" style="font-size: 15px; color: #7f7f7f">O: 383.384.4838 ext.</div>
<div class="sig__field sig__office set-inform" style="font-size: 15px; color: #7f7f7f"></div><span> </span>
<div class="sig__phone set-inform" style="font-size: 15px; color: #7f7f7f"></div>
<div class="links-text" style="font-size: 15px;"><a style="color: #7f7f7f;" href="#">FB</a> | <a style="color: #7f7f7f;" href="#">Twitter</a> | <a style="color: #7f7f7f;" href="#">Instagram</a> | <a style="color: #7f7f7f;" href="#">LinkedIn</a></div>
</td>
</tr>
<tr border="0" cellpadding="0" cellspacing="0">
<td align="center" style="mso-table-lspace:0pt;mso-table-rspace:0pt;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%;font-family:Arial,sans-serif;font-size:16px;font-weight:normal;color:#313030;line-height:1.5;margin:0;padding:0;" valign="top">
<table border="0" cellpadding="0" cellspacing="0" style="border-collapse:collapse;mso-table-lspace:0pt;mso-table-rspace:0pt;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%;font-family:Helvetica,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:normal;color:#313030;line-height:1.5;">
<tbody border="0" cellpadding="0" cellspacing="0">
<tr border="0" cellpadding="0" cellspacing="0">
<td align="left" style="mso-table-lspace:0pt;mso-table-rspace:0pt;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%;line-height:1.4;" valign="top">
New York University
<span style="margin-left:10px;margin-right: 10px;border-left: solid; border-color: #002857;border-width: 2px;"></span><span style="font-weight: bold;color: #C20F2F;font-size:18px;letter-spacing: 1px;"> NYU</span>
</td>
</tr>
<tr border="0" cellpadding="0" cellspacing="0">
<td align="left" height="16" style="mso-table-lspace:0pt;mso-table-rspace:0pt;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%;" valign="top">
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</section>
<section class="sig-gen__section">
<button id="copy" class="button--copy"></button>
<button id="reset" class="button--reset"><i class="fas fa-sync"></i> Reset Form</button>
</section>
<section class="sig-gen__section">
<div id="instructions"></div>
</section>
</div>

Function not working after push() elements in html file

I want to create a to do list that will add elements typed in <input type="text"> and delete when clicked on button with class .delete. When ever I push elements in an array. And innerHTML it in html page, the delete button stops working. The delete button works for elements that are written into Html code. If someone can help me I will be very thankful.
`
const itemsLIst = document.querySelector('.item-list'); // where we want to add our list
const addText = document.querySelector('.submit'); // add button
let deleteText = document.querySelectorAll('.delete'); // delete button
// const list = JSON.parse(localStorage.getItem('items')) || [];
let list = [];
function addItem(e) {
let text = document.querySelector('.input_bar').value; //text typed in input bar
if (text.length != 0) {
list.push(`<div>
<p>${text}</p>
<button class="delete" onclick='deleteItem'>🗴</button>
<button class="edit">Edit</button>
</div><hr>`);
itemsLIst.innerHTML = list.join('');
text = '0';
document.getElementById("myText").value = "";
} else {
return;
}
}
function deleteItem(e) {
this.parentElement.style.display = 'none';
}
for (var i = 0 ; i < deleteText.length; i++) {
deleteText[i].addEventListener('click', deleteItem);
}
addText.addEventListener('click', addItem);
<style>
body {
width: 100%;
height: 100vh;
background-color: rgb(115, 115, 197);
margin: 0;
padding: 0;
position: relative;
}
.container {
width:50%;
height:70%;
position: absolute;
background-color: rgb(241, 241, 241);
font-family: Arial, Helvetica, sans-serif;
border-bottom-left-radius: 25px;
border-bottom-right-radius: 25px;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
overflow-y: scroll;
}
.heading {
width: 100%;
height: 122px;
background-color: #5B45B9;
display: flex;
align-items: center;
justify-content: center;
}
.heading h1 {
color: white;
font-size: 40px;
}
.item-list {
width: 100%;
padding: 0 0 30px 0;
}
.item-list div {
width: auto;
height: 60px;
}
p {
width: 60%;
float: left;
font-size: 25px;
padding-left: 30px;
margin-top: 12px ;
}
.item-list button {
width: 60px;
height: 60px;
font-size: 18px;
float: right;
}
.delete {
font-size: 30px;
color: red;
}
.input_form {
width: 100%;
padding: 30px 0 30px 0;
position: absolute;
bottom: 0;
text-align: center;
}
.input_form .input_bar {
width: 80%;
height: 50px;
font-size: 18px;
border: none;
}
.input_form button {
width: 10%;
height: 50px;
float: right;
margin-right: 30px;
}
</style>
<html>
<head>
</head>
<body>
<div class="container">
<div class="heading">
<h1>TO-DO LIST</h1>
</div>
<div class="item-list">
<div>
<p>TEEXT2</p>
<button class="delete">🗴</button>
<button class="edit">Edit</button>
</div>
<div>
<p>TEEXT1</p>
<button class="delete">🗴</button>
<button class="edit">Edit</button>
</div>
<div>
<p>TEEXT3</p>
<button class="delete">🗴</button>
<button class="edit">Edit</button>
</div>
<div>
<p>TEEXT4</p>
<button class="delete">🗴</button>
<button class="edit">Edit</button>
</div>
</div>
<div class="input_form">
<input type="text" class="input_bar" id="myText" placeholder="Add ITEM">
<button class="submit">+ADD ITEM</button>
</div>
</div>
</body>
</html>
<button class="delete">🗴</button>
<button class="edit">Edit</button>
</div>
<div>
<p>TEEXT1</p>
<button class="delete">🗴</button>
<button class="edit">Edit</button>
</div>
<div>
<p>TEEXT3</p>
<button class="delete">🗴</button>
<button class="edit">Edit</button>
</div>
<div>
<p>TEEXT4</p>
<button class="delete">🗴</button>
<button class="edit">Edit</button>
</div>
</div>
<div class="input_form">
<input type="text" class="input_bar" id="myText" placeholder="Add ITEM">
<button class="submit">+ADD ITEM</button>
</div>
<script src="script.js"></script>
</div>
</body>
</html>`
You actually only trigger DOM "original" delete button (button loaded with your HTML code) with the line :
let deleteText = document.querySelectorAll('.delete'); // delete button
Your others .delete are loaded after the first DOM loading and are not even listed in "deleteText" array !
You have to refresh deleteText every time you add a new item. Something like :
const itemsLIst = document.querySelector('.item-list'); // where we want to add our list
const addText = document.querySelector('.submit'); // add button
let deleteText = document.querySelectorAll('.delete'); // delete button
// const list = JSON.parse(localStorage.getItem('items')) || [];
let list = [];
function addItem(e) {
let text = document.querySelector('.input_bar').value; //text typed in input bar
if (text.length != 0) {
list.push(`<div>
<p>${text}</p>
<button class="delete" onclick='deleteItem'>🗴</button>
<button class="edit">Edit</button>
</div><hr>`);
itemsLIst.innerHTML = list.join('');
text = '0';
document.getElementById("myText").value = "";
} else {
return;
}
}
function deleteItem(e) {
this.parentElement.style.display = 'none';
}
function triggerDeleteButton(){
deleteText = document.querySelectorAll('.delete'); // delete button
for (var i = 0 ; i < deleteText.length; i++) {
deleteText[i].addEventListener('click', deleteItem);
}
}
addText.addEventListener('click', function(){
addItem() ;
triggerDeleteButton() ;
}
);
Without refreshing, you can add and edit data by using local storage
For example, like below, you can try once!
<script>
let customerData = [];
// Inserting new customer record into local storage
function insert() {
let company = document.getElementById("company").value;
let obj = {company};
customerData.push(obj);
synData(customerData);
let customerDetails = JSON.parse(localStorage.getItem("customerString"));
clearFileds();
displayelements(customerDetails);
}
function displayelements(customerDetails) {
let html = "<table id='customer_data' border='1'><tr><th>Sl No</th><th>Company</th><th>Delete</th></tr>";
if(customerDetails == '') {
html+="<tr>No record found!</tr>";
} else {
customerDetails.map((values, index) => {
html+="<tr id='row_data'>";
html+="<td>"+index+"</td>";
html+="<td>"+values.company+"</td>";
html+="<td onclick='deleteRow(" + index + ")'>Delete</td>";
html+="</tr>";
} )
}
html+="</table>";
document.getElementById("display").innerHTML = html;
clearFileds();
}
// Delete the specific customer record from local storage
function deleteRow(deleteKey) {
let customerDetails = JSON.parse(localStorage.getItem("customerString"));
customerDetails.map((values, index) => {
if (index == deleteKey) {
customerDetails.splice(index, 1);
}
})
customerData = customerDetails;
synData(customerDetails);
displayelements(customerDetails);
}
// Clearing the form input field data
function clearFileds() {
document.getElementById("company").value = '';
}
// Updating local storage data
function synData(customerDetails) {
localStorage.setItem('customerString', JSON.stringify(customerDetails));
}
</script>
<html>
<head>
<title>Save</title>
</head>
<script ></script>
<body id="format_background">
<div id="customerAction" >
<h1>Customer data</h1>
<label>Company Name </label>
<input id="company" type="text" />
<button type="button" value="Save&Show" onclick="insert()" id="insert">Save</button>
</div>
<div id="display"></div>
</body>
</html>

Enter the prototype of an Object.prototype.function(), where all the EventListeners are safed (Javascript)

I am working on an assignment, that constructs a small library. The project requires, that the user can input the title of a book, its author, number of pages and if the user has already read it. Then the content gets displayed on the page.
Here's the code (work in progress):
let myLibrary = [];
let submitBtn = document.querySelector("#submitBtn");
let textInput = document.querySelectorAll("input");
let addNew = document.getElementById("addNew");
let fieldSet = document.getElementById("fieldset");
let cancelBtn = document.querySelector("#cancelBtn");
let bookDisplay = document.getElementById("bookDisplay");
let flexItems = document.getElementsByClassName("flexItems");
// object Constructor for new books
class Book {
constructor(title, author, pages, read) {
this.title = title;
this.author = author;
this.pages = pages;
this.read = read;
}
}
Book.prototype.addToDisplay = function() {
let newDiv = document.createElement("div");
bookDisplay.appendChild(newDiv).className = "flexItems";
let newSpan = document.createElement("span");
flexItems[myLibrary.length-1].appendChild(newSpan).className = "spanItem";
newSpan.innerText = this.title;
this.read === true ? flexItems[myLibrary.length-1].style.backgroundColor = "green" :
flexItems[myLibrary.length-1].style.backgroundColor = "red";
newDiv.addEventListener("mouseenter", moreInfo => {
newSpan.childNodes[0].nodeValue = this.author + "\n" + this.title + "\n" + this.pages + " pages";
})
newDiv.addEventListener("mouseleave", defaultInfo => {
newSpan.childNodes[0].nodeValue = this.title;
})
}
// creates a new instance of Book and pushes the object into the array
let addToLibrary = function addToLibrary() {
newBook = new Book(textInput[0].value, textInput[1].value, textInput[2].value, textInput[3].checked)
myLibrary.push(newBook);
newBook.addToDisplay();
};
// eventlistener, to submit a new Book to the library
submitBtn.addEventListener("click", addToLibrary);
// sets the form's display from block to non-visible
let cancel = function cancel() {
fieldSet.style.display = "none";
}
// cancels the form and returns back
cancelBtn.addEventListener("click", cancel);
// sets the form's display from non-visible to visible
let openForm = function openForm() {
fieldSet.style.display = "block";
}
// opens form to add new book
addNew.addEventListener("click", openForm);
body {
margin-left: 20px;
}
h1 {
text-align: center;
}
#fieldset {
position: fixed;
z-index: 2;
border: none;
display: none;
background: #3CBC8D;
border-radius: 10px;
right: 1%;
top: 2%;
width: 400px;
height: auto;
overflow: auto;
}
button {
cursor: pointer;
}
.display {
display: flex;
flex-direction: row;
flex-wrap: wrap;
position: relative;
}
.flexItems {
position: relative;
display: flex;
margin: 5px;
color: black;
font: Georgia;
font-size: 20px;
height: 200px;
width: 200px;
align-items: center;
border: 2px solid gray;
transition: 500ms;
border-radius: 5px;
}
.spanItem {
width: 100%;
text-align: center;
white-space: wrap;
overflow: hidden;
text-overflow: ellipsis;
}
.display .flexItems:focus,
.display .flexItems:hover {
transform: scale(1.2);
z-index: 1;
}
#addNew {
position: fixed;
z-index: 2;
border: none;
background: #3CBC8D;
color: white;
border-radius: 10px;
right: 2%;
top: 2%;
width: 100px;
height: 50px;
overflow: auto;
cursor: pointer;
}
/*. Could be additionally used for the hover-effect, but doesnt look that nice for more than one row
flexItems:hover ~.flexItems {
transform: translateX(25%);
}
.display:focus-within .flexItems,
.display:hover .flexItems {
transform: translateX(-25%);
}
.flexItems:focus ~.flexItems,
.flexItems:hover ~.flexItems {
transform: translateX(25%);
} */
<!DOCTYPE html>
<head>
<meta charset="UTF-8">
<link href="style.css" rel="stylesheet" type="text/css" />
<script src="script.js" defer></script>
<title>Library</title>
</head>
<body>
<div><h1>My book library</h1></div>
<div id="bookDisplay" class="display">
</div>
<div>
<button id="addNew">Test</button>
</div>
<fieldset id="fieldset">
<form id="form">
<div>
<label for="title">Title: </label>
<input type="text" name="title" id="title" class="usrInput">
</div>
<div>
<label for="author">Author: </label>
<input type="text" name="author" id="author" class="usrInput">
</div>
<div>
<label for="number">Number of pages: </label>
<input type="number" name="number" id="number" class="usrInput">
</div>
<div>
<label for="read">Already read?: </label><br>
Y <input type="radio" name="read" id="read" value="Y" class="read">
N <input type="radio" name="read" id="read" value="N" class="read">
</div>
<button id="submitBtn" type="button">Submit</button>
<button id="cancelBtn" type="button">Cancel</button>
</fieldset>
</div>
</body>
</html>
**My question: ** The code is working until here. But I have my concerns with the mouseenter-Eventlistener. On mouseenter, I want to add a <button>, to edit the book's value. And maybe there is moreto be added in the future. That would mean the àddToDisplay() function eventually will get clunky. So I was wondering, if I could enter the Object.prototype of the addToDisplay() function and store all eventListeners in its prototype. Is this possible in Javascript?
The only way I could solve it right now is, to write an extra function for the eventlisteners. But it seems, that this way, I'm just going back to normal function expressions with dozens of values to pass by:
Book.prototype.addToDisplay = function() {
// (...)
mousehover(this, newDiv, newSpan)
}
let mousehover = function mousehover(test, newDiv, newSpan) {
newDiv.addEventListener("mouseenter", moreInfo => {
newSpan.childNodes[0].nodeValue = test.author + "\n" + test.title + "\n" + test.pages + " pages";
})
newDiv.addEventListener("mouseleave", defaultInfo => {
newSpan.childNodes[0].nodeValue = test.title;
})
}
Hope I got the problem across. It's the first assignment to Objects I'm working on.
Thanks for any answers and links to informative sources.

How do I delete only one row of appended information in a table using jQuery?

I am trying to make a "My Favorite Movies" list page where users can add and rate movies. This program should include:
1) a form where you can add to the list and rate it
2) a table of all the things you've added
3) delete button for each row of the table that lets you remove elements from the list (what i'm having trouble on)
Instead of deleting only one row, it deletes every appended movie/rating in the table. Also if you click anywhere else, it deletes everything as well.
4) bonus: sort feature, so i can sort entries in the table by the their title or their rating.
example here: rithm school example
$(function() {
$('#addMovieButton').click(function() {
var addToTitle = $('#title').val();
$('#tableTitle').append('<tr><td>' + addToTitle + '</td></tr>');
var addToRating = $("#rating").val();
$('#tableRating').append('<tr><td>' + addToRating + '</td></tr>');
$('#tableDelete').append('<tr><td><input type="button" value="Delete All"</tr></td>');
$('#tableRow').on('click', function() {
$('#tableTitle').last().children().remove();
$('#tableRating').last().children().remove();
$('#tableDelete').last().children().remove();
});
});
});
h1 {
text-align: center;
}
table {
width: 100%;
border-radius: 10px;
}
table,
td {
border: 1px solid black;
padding: 15px;
}
th {
height: 50px;
text-align: center;
}
td {
text-align: center;
}
body {
font-family: helvetica;
}
form {
text-align: center;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<form>
<label><b>Title</b></label>
<input id="title" type="text" value="Movie Title">
<label><b>Rating</b></label>
<input id="rating" type="text" value="Rate The Movie from 0 to 10">
<button type='button' id="addMovieButton">Add Movie</button>
</form>
<table>
<tr id="tableRow">
<th id="tableTitle">Title</th>
<th id="tableRating">Rating</th>
<th id="tableDelete">Delete</th>
</tr>
</table>
<table> Structure
The structure of the appended "row" are not valid HTML. A <table> will have at least one <tbody>. If the user doesn't add it the browser will. Although most methods, function, and properties will treat the <table> as the direct parent of <tr>, there are some advantages to targeting <tbody> instead. If there's a <thead> then targeting the <tbody> can free you from extra steps trying to avoid the <th>.
Keep these rules in mind when structuring a <table>
<tbody> can only have <tr> as children (direct descendants)
<tr> can only have <td> and <th> as children
<td> and <th> can have anything as descendants.
Make sure rows are structured like so:
<tr><td</td>...<td></td></tr>
Add row to <table> or <tbody>
Demo
The following demo has detailed comments within the HTML, and CSS, as well as step by step details commented in the JavaScript
$(function() {
/*
Bind the <form> to the 'submit', 'click', and 'change' events.
Pass the Event Object thru
*/
$('form').on('submit click change', function(event) {
// Reference the type of event
let eType = event.type;
// if the 'submit' event was triggered...
if (eType === 'submit') {
// Stop the <form> from sending data to a server and resetting
event.preventDefault();
// Get the values of the <input>
let name = $('.title').val();
let rate = $('.rating').val();
// Declare a htmlString using a Template Literal
const row = `
<tr><td>${name}</td>
<td>${rate}</td>
<td><input class='sel' type='checkbox'>
</td></tr>`;
// Render the htmlString as the last child of the <tbody>
$('.data').append(row);
// Reset <form>
$(this).trigger('reset');
// ...otherwise if the 'click' event triggered...
} else if (eType === 'click') {
// ...and the clicked tag has class 'del'...
if ($(event.target).hasClass('del')) {
/*
Collect all checked <input class='sel'>
then on .each() one...
*/
$('.sel:checked').each(function(i) {
/*
Get the ancestor <tr> of the current .sel
and remove it
*/
$(this).closest('tr').remove();
});
// Reset the <form>
$('form').trigger('reset');
}
// ...otherwise if the 'change' event was triggered...
} else if (eType === 'change') {
// ...and the changed tag id is 'all'...
if (event.target.id === 'all') {
// Check if #all is checked or not
let allChk = $('#all').is(':checked');
// Loop thru each() <input class='sel'>...
$('.sel').each(function(i) {
/*
and check current .sel if #all is checked
or uncheck current .sel if #all is NOT checked
*/
$(this).prop('checked', allChk);
});
}
}
// Stop any events from bubbling any further up the event chain
event.stopPropagation();
});
});
:root {
font: 400 3vw/1.2 Arial;
}
form {
text-align: center;
}
table {
width: 100%;
border-radius: 10px;
table-layout: fixed;
margin: 12px auto
}
table,
td {
border: 1px solid black;
padding: 15px;
}
th {
height: 30px;
width: 20%;
}
th:first-of-type {
width: 60%;
}
td {
text-align: center;
}
button,
input,
label {
display: inline-block;
font-size: initial;
}
.all {
font-weight: 400;
padding: 3px 6px;
border: 1.5px inset rgba(0, 28, 255, 0.3);
margin-top: 3px;
}
.all::after {
content: 'Selected'
}
/*
When input#all is :checked the label.all that follows
#all will change
the content of its pseudo-element from 'Selected' to 'All'
*/
#all:checked+.all::after {
content: 'All'
}
button:hover {
cursor: pointer;
outline: 3px outset rgba(0, 28, 255, 0.4);
color: rgba(0, 28, 255, 0.6);
}
.all:hover {
cursor: pointer;
color: rgba(0, 28, 255, 0.8);
background: rgba(0, 28, 255, 0.2);
}
.rating {
text-align: right;
width: 4ch;
}
.title {
padding-left: 5px;
width: 27ch;
}
/*
The checkbox #all is not visible to user but is accessible through the label.all
which it is synced with (see comments in HTML
*/
#all {
display: none
}
<form>
<label>Title</label>
<!--
The [required] attribute enables built-in form validation
If the submit event is triggered
and either <input> is blank, the submit event is interrupted and
a tooltip will notify
user that the <input> cannot be empty
-->
<input class="title" type="text" placeholder="Pulp Fiction" required>
<label>Rating</label>
<!-- See previous comment -->
<input class="rating" type="number" min='0' max='10' placeholder="10" required>
<!--
<button type='submit'> or <input type='submit'>
or <button> within a <form> will trigger a submit event by default
-->
<button>Add Movie</button>
<table>
<thead>
<tr>
<th>Title</th>
<th>Rating</th>
<th>
<button class='del' type='button'>Remove</button>
<!--
A <label> and a form control (ie <input>, <textarea>, <select>, etc) can be synced by
matching the [for] attribute value to the form controls #id:
1. <label for='XXX'>
2. <input id='XXX'>
When synced, clicking one will remotely click the other
-->
<input id='all' type='checkbox'>
<label class='all' for='all'></label></th>
</tr>
</thead>
<!--
See post on <table> structure
-->
<tbody class='data'></tbody>
</table>
</form>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
you've put all titles in a parent, and all rating in another parent ,and all delete buttons in another one . you should place the information about each row in a parent and then you can delete by row easily.
(also you can add td,tbody it's just sample to showing the way)
$('#addMovieButton').click(function () {
var addToTitle = $('#title').val();
var addToRating = $("#rating").val();
$('#table').append('<tr><th>' + addToTitle + '</th><th>' + addToRating + '</th><th><input type="button" value="Delete All" class="tableDelete"></th></tr>');
$('.tableDelete').click(function () {
$(this).parents('tr').remove();
});
});
h1 {
text-align: center;
}
table {
width: 100%;
border-radius: 10px;
}
table,
td {
border: 1px solid black;
padding: 15px;
}
th {
height: 50px;
text-align: center;
}
td {
text-align: center;
}
body {
font-family: helvetica;
}
form {
text-align: center;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<form>
<label><b>Title</b></label>
<input id="title" type="text" value="Movie Title">
<label><b>Rating</b></label>
<input id="rating" type="text" value="Rate The Movie from 0 to 10">
<button type='button' id="addMovieButton">Add Movie</button>
</form>
<table id="table">
</table>

Getting undefined in Dom but value available in Console

$(function () {
var people = [];
var ctCells = [], questionCells = [], userCells = [];
var $tBody = $("#userdata tbody");
$.getJSON('https://api.myjson.com/bins/18g7fm', function (data) {
$.each(data.ct_info, function (i, f) {
ctCells.push(`<td id=${f.id}>${f.id}</td><td>${f.name}</td>`);
var users = []
var question = []
f.Qid_info.forEach((x) => {
x.user_info.forEach((y) => {
//avoid duplicates
var foundUser = users.find((user) => {
return user.id === y.id
})
if (!foundUser) {
users.push(y)
}
})
})
f.Qid_info.forEach((x) => {
var foundQuestion = question.find((questions) => {
return questions.id === x.id
})
if (!foundQuestion) {
question.push(x)
}
})
$.each(question, function (i, question) {
ctCells.push(`<td colspan="2"> </td>`)
questionCells.push(`<td id=${question.id}>${question.id}</td><td>${question.isActive}</td><td>${question["is complex"]}</td><td>${question["is breakdown"]}</td>`);
})
$.each(users, function (i, user) {
var a = user.data.map(function (key) {
return key
})
// ctCells.push(`<td colspan="2"> </td>`)
// questionCells.push(`<td colspan="${lent+1}"> </td>`)
userCells.push(`<td><div style="display:flex; flex-direction:row">
${
users.forEach(val => {
`<div style="display:flex; flex-direction:column">
<div>${val.id}${val.name}</div>
<div>${val.updatedAt}</div>
<div style="display:flex; flex-direction:column">
${user.data.forEach(IN => {
`
<div style="display:flex; flex-direction:row">
<div><p>${console.log(IN.git_ids)}</p></div>
</div>
`
})}
</div>
</div>`
})
}
</div></td>`)
// userCells.push(`<td id=${user.id}>UserId--- ${user.id} UserName---- ${user.name}${a.map(value => {
// return `
// <div id="${user.id}" >
// <td><input type="checkbox" style="display: inline;"> </td>
// <td> <span id="text">${Object.keys(value)[0]}</span></td>
// <td> <textarea type="text" class="gitplc" placeholder="GitId">${ value.git_ids}</textarea> </td> j
// </div>
// `
// })
// }</td><td>${user.updatedAt}</td>`);
})
});
console.log(userCells)
$.each(ctCells, function (i) {
console.log(userCells)
$tBody.append(`<tr>${ctCells[i]}${questionCells[i]}${userCells[i]}</tr>`)
})
});
});
#scrlSec {
overflow-x: scroll;
overflow-y: hidden;
white-space: nowrap;
}
/* .checkSec { width: 60%; } */
.checkSec .tbl {
padding-top: 20px;
}
.checkSec td {
padding-left: 10px;
}
.checkSec .btnGreen {
color: white;
background-color: #4CAF50;
border: none;
padding: 15px 32px;
width: 100%;
text-decoration: none;
}
.checkSec .gitplc {
width: 80%;
}
.checkSec #text {
font-size: 14px;
}
.checkSec .tbl .colOne {
width: 50%;
float: left;
}
.checkSec .tbl .colTwo {
width: 50%;
float: right;
}
#user {
overflow: auto;
}
.flex-container {
display: flex;
}
th,
td {
font-weight: normal;
padding: 5px;
text-align: center;
width: 120px;
vertical-align: top;
}
th {
background: #00B0F0;
}
tr+tr th,
tbody th {
background: #DAEEF3;
}
tr+tr,
tbody {
text-align: left
}
table,
th,
td {
border: solid 1px;
border-collapse: collapse;
table-layout: fixed;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<table id='userdata'>
<thead>
<tr>
<th colspan="2" id="ct">CT INFO</th>
<th colspan="4" id="que">Question</th>
<th id="user">User Info</th>
</tr>
<tr>
<th>CT ID</th>
<th>CT</th>
<th>Id</th>
<th>isActive</th>
<th>is Complex</th>
<th>is Breakdown</th>
<th>USER</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
I am printing json data in table format in html. But in users column i am getting undefined but when i print those variable in console that is printing correct value.
This is my json api . When i print value in foreach loop of user i am getting undefined but on console.log i have proper value printing in console.
https://api.myjson.com/bins/18g7fm
This is happening because you are using forEach() inside a template literal. forEach() returns undefined by default. Use map() function instead.
userCells.push(`<td><div style="display:flex; flex-direction:row">
${
users.map(val => {
return `<div style="display:flex; flex-direction:column">
<div>${val.id}${val.name}</div>
<div>${val.updatedAt}</div>
<div style="display:flex; flex-direction:column">
${user.data.map(IN => {
return `
<div style="display:flex; flex-direction:row">
<div><p>${console.log(IN.git_ids)}</p></div>
</div>
`
})}
</div>
</div>`
})
}
</div></td>`)

Categories

Resources