Clone on click does not produce new DOM element - javascript

I've got this simple JS script what I need to achieve is: every time the button is clicked a clone of the card is appended to container.
const card = document.querySelector('.card').cloneNode(true)
const button = document.querySelector('.btn')
const container = document.querySelector('.container')
button.addEventListener('click', () => {
container.append(card)
})
<div class="container">
<button class="btn">this is a button</button>
<div class="card">
<h1>This is a card</h1>
</div>
</div>
Nothing too hard. The first time I click on button everything work fine.
Why the next time the button doesn't append a new clone?

You are reinserting the same clone over and over again. You need to create a new clone every time the button is clicked.
.container {
background: #ddd;
padding: 1em;
}
.card {
display: inline-block;
width: 5em;
height: 10em;
font-size: 10px;
border: 2px solid black;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div class="container">
<button class="btn">this is a button</button>
<div class="card">
<h1>This is a card</h1>
</div>
</div>
</body>
<script>
const button = document.querySelector('.btn')
const container = document.querySelector('.container')
button.addEventListener('click', () => {
const card = document.querySelector('.card').cloneNode(true)
container.append(card);
})
</script>
</html>

You only made one single clone before the click happened. You should make a new clone on each click.
So move the assignment to card inside the click handler:
const button = document.querySelector('.btn');
const container = document.querySelector('.container');
button.addEventListener('click', () => {
const card = document.querySelector('.card').cloneNode(true);
container.append(card);
});
Unrelated, but please terminate your statements with ;. You don't want to rely on the automatic semicolon insertion mechanics, which can sometimes have surprising effects.

Related

How to change height of text area on clicking a button?

I have an automatically expanding text-area. But when clicking send button, text-area is not going back to original height. I have included a sample code. In the project, its implemented using react. Is there any way to make text area height to "50px" when clicking send button? Thank you
var textarea = document.querySelector('textarea');
textarea.addEventListener('keydown', autosize);
function autosize() {
var el = this;
setTimeout(function() {
el.style.cssText = 'height:auto; padding:0';
el.style.cssText = 'height:' + el.scrollHeight + 'px';
}, 0);
}
.textarea {
overflow: hidden;
padding: 10px;
width: 250px;
font-size: 14px;
margin: 50px auto;
display: block;
border-radius: 10px;
border: 6px solid #556677;
}
<!DOCTYPE html>
<html lang="en">
<head>
<title>Bootstrap Example</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap#4.6.1/dist/css/bootstrap.min.css">
</head>
<body>
<div class="container-fluid pl-5">
<div class="row w-70 text-center">
<textarea rows='1' placeholder='Auto-Expanding Textarea'></textarea>
</div>
<button class="send-btn" onClick={()=> messageSendHandler()}>send</button>
</div>
</body>
</html>
I don't see the messageSendHandler() function description attached to your post. Does it clear the textarea field? If so, when it happens, the keydown event does not occur, and thus the autosize() function is not triggered. So, I can see these 2 ways to choose from:
if you'd like to run this autosize() function on form submit as well, replace keydown event with input event — it is more universal and can help with different input types (such as dictation),
another option would be to reset the textarea height inside the form submit function (messageSendHandler()), similarly to how you do it here:
el.style.cssText = 'height:' + el.scrollHeight + 'px';
Also, alternatively, maybe this CSS-tricks URL can give you more inspiration on the topic.

Fetch Request Causing Page reload

My page keeps on reloading after my fetch request is complete. I don't want the page to reload after I submit the form. Can anyone help out as to why even after using e.preventDefault() I get that behavior? Also can you suggest better formatting tips for JS as I'm a beginner and would like your input. I'm fetching the data from a fake REST API made using json-live-server
HTML
<!DOCTYPE HTML>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="styles.css">
<title>Document</title>
</head>
<body class="body">
<h1> Vaccination Centers</h1>
<div id='app'>
<form id='form'>
<input type="text" id='enrollment' />
<input type='text' id='session' />
<button type="submit">submit</button>
</form>
</div>
<script type="module" src="main.js"></script>
</body>
</html>
JS
let listArray
function getCenters () {
fetch ('http://localhost:3001/students')
.then(
response => response.json()
)
.then(data => {
listArray = data
console.log(listArray)
})
};
function init () {
getCenters()
const form = document.getElementById('form')
form.addEventListener('submit', (e) => validate(e))
}
function validate (e) {
console.log(e)
e.preventDefault()
let enrollment = document.getElementById('enrollment').value
let student = listArray.find(s => s.enrollment.toString() === enrollment.toString())
fetch ('http://localhost:3001/students/' + student.id, {
method: 'PATCH',
headers: {
'Content-type': 'application/json; charset=UTF-8' // Indicates the content
},
body: JSON.stringify({ paidFee: true })
}).then(document.getElementById('app').innerHTML = 'HELLO BITCHES')
}
window.onload = init
We can all agree that e.preventDefault() placed at the first line  ⃰ in the event handler (aka validate(e)) will stop the default browser behavior of a <form> when a "submit" event is triggered on it. The behavior OP is observing is the destructive overwriting of the following:
.then(document.getElementById('app').innerHTML = 'HELLO BITCHES')
Just remove the above and you shouldn't have what appears as a page reload, but if you must say, "HELLO" to the bitches, use .insertAdjacentHTML() instead:
.then(document.getElementBYId('app').insertAdjacentHTML('beforeBegin', 'HELLO BITCHES'))
The following example has an event handler for the "submit" (submitter(e)) and "click" (clicker(e)) events. If button#A is clicked, .innerHTML is used and if button#B is clicked, .insertAdjacentHTML() is used. There is also a logger function (eventLogger(e)) which will log:
type of event........................................e.type
event listener #id............................e.currentTarget
button #id..........................................e.target.id (from 'click' event)
if default was prevented or not.....e.defaultPrevented
⃰actually it's console.log() at that position but there's no difference in this context
Best viewed in Full Page mode
document.forms[0].addEventListener('submit', submitter);
document.querySelectorAll('button').forEach(node => node.addEventListener('click', clicker));
function submitter(e) {
e.preventDefault();
let ID = this.elements.AB.value;
const APP = document.getElementById('app');
switch (ID) {
case 'A':
APP.innerHTML = 'HELLO BITCHES - #app is gutted by .innerHTML - everything within #app is overwritten';
break;
case 'B':
const htmlString = 'HELLO BITCHES - this is added in front of #app - nothing is overwritten bitches';
APP.insertAdjacentHTML('beforeBegin', htmlString);
break;
default:
break;
}
eventLogger(e);
};
function clicker(e) {
document.forms[0].AB.value = e.target.id;
};
function eventLogger(e) {
let ID = e.target.elements.AB.value;
console.clear()
let log = 'Event Type: ' + e.type + '\nEvent Listener ID: ' + e.currentTarget.id + '\nButton ID: ' + ID + '\nDefault Prevented: ' + e.defaultPrevented;
console.log(log);
};
body {
padding: 8px;
font: 1ch/1 Consolas;
}
h1 {
font-size: 1.75ch/1;
}
#app {
margin: 8px;
padding: 20px;
outline: 3px dashed blue;
}
form {
padding: 20px;
outline: 3px dotted red
}
input {
display: inline-block;
width: 15ch;
}
button {
display: inline-block;
width: 6ch;
cursor: pointer;
}
p {
display: inline-block;
}
p:first-of-type {
color: blue;
}
p:last-of-type {
color: red;
}
code {
font-weight: bold;
color: #ab00ef;
}
/* SO Console Display - Right Side Column */
.as-console-wrapper {
width: 50% !important;
max-height: 100%;
margin: 0 0 25% 50%;
font-size: 0.8ch/1;
font-variant: normal;
}
.as-console-row.as-console-row::after {
content: '';
padding: 0;
margin: 0;
border: 0;
width: 0;
}
<!DOCTYPE HTML>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body class="body">
<h1>Vaccination Centers</h1>
<p>Blue dashed outline is <code>#app</code></p>
<p>Red dotted outline is <code>#form</code></p><br>
<div id='app'>
<form id='form'>
<input type="text" id='enrollment'>
<input type='text' id='session'>
<button id='A'>A</button>
<button id='B'>B</button>
<input id='AB' type='hidden'>
</form>
</div>
</body>
</html>
There were a few bits that needed some attendence:
In your original script you used an asynchronous fetch to define the variable listArray. Unfortunately you did not wait for the value to be put into that variable but continued straight away. This "awaiting" can only happen in an asynchronous function. Therefore:
I created an async function as this makes it much easier to process promises with await inside.
The first one fills listArray with all the registered students for comparison with an entered name
The comparison needs to be done on enrollment.value.trim().toLowerCase() (there is no .toString() involved)
If the name was found, a second fetch() command is sent, "PATCHing" the new information to the server
The return data from the server is then displayed in JSON format under the "success message".
const api='http://jsonplaceholder.typicode.com/users/';
document.getElementById('form').addEventListener("submit",validate);
async function validate(e) {
e.preventDefault();
const listArray=await fetch(api).then(r=>r.json());
let student= listArray.find(s=>s.username.toLowerCase()===enrollment.value.trim().toLowerCase())
if (student) {
const d = await fetch(api+student.id, {method: 'PATCH',headers: {'Content-Type': 'application/json'},body: JSON.stringify({paidFee:true})}).then(r=>r.json());
document.getElementById('app').innerHTML = '<div>HELLO BITCHES</div><pre>'+JSON.stringify(d,null,2)+'</pre>';
} else console.log("This student does not exist in our list: "+listArray.map(s=>s.username).join(" "));
}
<h1> Vaccination Centers</h1>
<div id='app'>
<form id='form'>
<input type="text" id='enrollment' value="Bret">
<input type='text' id='session'>
<button type="submit">submit</button>
</form>
</div>

Can I install SPIFFS for Wemos D1 Mini Lite (ESP8285 chip) to use JS, HTML and CSS files as a stand alone webserver

I am looking to create a standalone webserver using the Wemos D1 Mini Lite (this is the only wifi microcontroller I have and cant afford a different one atm).
I know SPIFFS works with ESP32 chips, but can I use it with ESP8285 chips?
Looking to have HTML and CSS for the webserver, with JS scripts to run functions (At the moment only functionality is turning LEDs off and on), all uploaded to the Wemos and run from that.
HTML CODE
<!DOCTYPE html>
<html lang="en">
<head>
<link rel="stylesheet" href="./skeleton.css">
<link rel="stylesheet" href="./theme.css">
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div class = "mainDiv border" >
<div class = "headingDiv border">
<h3 id = "Header"> LED ON AND OFF </h3>
</div>
<div class = "checkmarkDiv border">
<div class = "row">
<label for ="colour_red">RED LED</label>
<input type = "checkbox" id = "colour_red">
</div>
<div class = "row">
<label for ="colour_yellow">YELLOW LED</label>
<input type = "checkbox" id = "colour_yellow">
</div>
<div class = "row">
<label for ="colour_green">GREEN LED</label>
<input type = "checkbox" id = "colour_green">
</div>
</div>
<div class = "buttonDiv border">
<button class = "button-primary" id = "button_ToggleLED"> Turn LED: ON </button>
</div>
</div>
</body>
<script src = "./mainJS.js"></script>
</html>
JS CODE
const button_LED = document.getElementById( "button_ToggleLED" )
const cb_red = document.getElementById ( "colour_red" )
const cb_yellow = document.getElementById( "colour_yellow" )
const cb_green = document.getElementById( "colour_green" )
let clickCheck = false
button_LED.addEventListener( "click", (event) => {
//consoleLEDStatus()
if (clickCheck) {
button_LED.innerHTML = "Turn LED: ON"
turnOFFLED()
}
else if (!clickCheck) {
button_LED.innerHTML = "Turn LED: OFF"
turnONLED()
}
clickCheck = !clickCheck
})
// A quick function you can run to check in dev console the status of LED
function consoleLEDStatus() {
console.log(`LED Status:
RED: ${cb_red.checked}
YELLOW: ${cb_yellow.checked}
GREEN: ${cb_green.checked}`)
}
function turnOFFLED() {
// Insert function to turn off LED
}
function turnONLED() {
// Insert function to turn on LED
}
CSS CODE
/* Test class for checking Div borders. Uncomment to see them*/
/*
.border{
border: black 2px solid;
}
*/
.mainDiv{
margin-left: 20%;
margin-right: 20%;
padding: 10px;
}
.checkmarkDiv{
padding: 10px;
}
.buttonDiv{
padding: 10px;
}
.headingDiv{
padding: 10px;
}
#Header{
}
#button_ToggleLED{
width: 200px;
float: center;
}
SPIFFS has been replaced with LittleFS (Little File System) and it works on EPS8266, See LittleFS for more details.
The code example for using LittleFS to serve webpage can be found at FSBrowser example. It is a quite comprehensive example, you probably only need to implement part of it.

Prevent from deleting the first paragraph inside a contenteditable div

I don't want to remove the first paragraph from the root div using the Backspace key. How can I prevent deleting the first or the only paragraph inside this contenteditable div? This paragraph should also editable. Javascript will help to prevent this paragraph. I need a idea.
.root {
max-width: 700px;
margin: 1rem auto;
border: 1px solid;
}
<div class="root" contenteditable="true">
<p id="block-1">I want to prevent this paragraph from delete.</p>
</div>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
.root {
max-width: 700px;
margin: 1rem auto;
border: 1px solid;
}
</style>
</head>
<body>
<div class="root" contenteditable="true">
<p id="block-1">I want to prevent this paragraph from delete.</p>
</div>
<script>
const div = document.querySelector('.root');
const paragraph = document.getElementById('block-1');
div.addEventListener(
'input',
(event) => {
if (!event.target.contains(paragraph)) {
div.insertBefore(paragraph, div.firstChild);
}
},
false
);
</script>
</body>
</html>
Adding the following event listener to the root element will prevent the backspace key from doing anything if all it can do is delete the first paragraph, which is the only time that it can delete the first paragraph. It starts by checking for the backspace key, but before preventing the default action from occurring, it will also check to make sure that there is no text and there is only one paragraph left. This works because paragraphs can only be deleted without text content, and you can't remove the first paragraph unless it is the only paragraph left.
document.getElementsByClassName("root")[0].addEventListener("keydown", function (event) {
if (event.key == "Backspace" && this.textContent == "\n \n" && this.children.length <= 1) {
event.preventDefault()
}
})
.root {
max-width: 700px;
margin: 1rem auto;
border: 1px solid;
}
<div class="root" contenteditable="true">
<p id="block-1">I want to prevent this paragraph from delete.</p>
</div>
You could add attribute contenteditable="false" either in html or with JS:
<div class="root" contenteditable="true">
<p id="block-1" contenteditable="false">I want to prevent this paragraph from delete.</p>
<p id="block-2">I do not want to prevent this paragraph from delete.</p>
</div>
or with JS:
let immutableP = document.getElementById('block-1');
immutableP.setAttribute("contenteditable", false);
Here is the link.

When button is clicked, the div jumps out of place

Problem
When I click the buttons for playoff season or regular, the divs that holds the content players-list and players-regular appear to jump out of place when they fade in and out. How do I prevent this from happening?
I've tried using position fixed on some of elements, but things would get way out of place. I've included a JSFiddle here: http://jsfiddle.net/onlyandrewn/gcthaffs/
Click listener
// Click listener, toggles between sheets
$('button').click(function() {
$('button').removeClass("active");
$(this).toggleClass("active");
if ($('button.regular').hasClass('active')) {
$('#players-list').fadeOut(500);
$('.note').fadeOut(500);
$('#players-regular').fadeIn(2000);
} else {
$('#players-regular').fadeOut(500);
$('#players-list').fadeIn(2000);
$('.note').fadeIn(2000);
}
});
index.html
<!DOCTYPE html>
<html lang="en" class="no-js">
<head>
<meta charset="UTF-8">
<title>Wheat Kings' leading point scorers</title>
<meta name="description" content="Wheat Kings' leading point scorers">
<meta name="author" content="Andrew Nguyen">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="assets/css/style.css">
<link href='http://fonts.googleapis.com/css?family=Lato:300,400,700,900' rel='stylesheet' type='text/css'>
</head>
<body>
<h1>Wheat Kings leading goal scorers</h1>
<p class="year"></p>
<button class="playoffs active">Playoffs</button>
<button class="regular">Regular Season</button>
<div class="top">
<div id="players-list"></div>
<div id="players-regular"></div>
<p class="note">Note: Since there was a five-way tie for 6th place, players who scored two goals were then ranked by their total points in the playoffs. The other two players not listed here are Nolan Patrick and Macoy Erkamps.</p>
</div><!-- /.top -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/tabletop.js/1.3.5/tabletop.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/3.0.0/handlebars.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/modernizr/2.8.3/modernizr.js"></script>
<!-- This is where the template for facts goes -->
<script id="players" type="text/x-handlebars-template">
<div class="container">
<div class="group">
<div class="{{row}}">
<p class="goals">{{goals}}</p>
<img src="{{image}}" alt="" class="head">
<p class="name">{{name}}</p>
<p class="position">{{position}}</p>
</div><!-- /.group -->
</div><!-- /.row -->
</div><!-- /.container -->
</script>
<script type="text/javascript">
// Click listener, toggles between sheets
$('button').click(function() {
$('button').removeClass("active");
$(this).toggleClass("active");
if ($('button.regular').hasClass('active')) {
$('#players-list').fadeOut(500);
$('.note').fadeOut(500);
$('#players-regular').fadeIn(2000);
} else {
$('#players-regular').fadeOut(500);
$('#players-list').fadeIn(2000);
$('.note').fadeIn(2000);
}
});
// Original
var public_spreadsheet_url = "https://docs.google.com/spreadsheets/d/1RMN49oyRlTxW5kv8MnYJwQRttis2csgVFH46kyORCaQ/pubhtml";
$(document).ready( function() {
Tabletop.init( { key: public_spreadsheet_url,
callback: showInfo,
parseNumbers: true } );
});
function showInfo(data, tabletop) {
var source = $("#players").html();
var template = Handlebars.compile(source);
// The actual name of the sheet, not entire .csv
$.each(tabletop.sheets("Playoffs").all(), function(i, fact) {
var html = template(fact);
// You need an element with this id or class in your HTML
$("#players-list").append(html);
$('.container').eq(i).addClass(data.Playoffs.elements[i]);
// This logs all the objects in the sheet
// console.log(data);
// This logs just validity
// console.log(data.Playoffs.elements[i]);
})
// If you need to get data from a second sheet in single Google Doc
$.each(tabletop.sheets("Regular").all(), function(i, fact) {
var html = template(fact);
// You need an element with this id or class in your HTML
$("#players-regular").append(html);
$('.container').eq(i).addClass(data.Regular.elements[i]);
// This logs all the objects in the sheet
// console.log(data);
// This logs just validity
// console.log(data.Regular.elements[i]);
});
}
</script>
</body>
</html>
base.scss
/*----------------------------------
MAIN STYLES
----------------------------------*/
html {
font-size: 62.5%; /* 10px browser default */
}
body {
max-width: 600px;
padding: 10px;
}
.top {
max-width: 600px;
}
#players-list,
#players-regular {
}
h1 {
font-family: 'Lato', sans-serif;
font-weight: 900;
border-bottom: 1px solid #ccc;
padding-bottom: 8px;
}
.note {
position: relative;
width: 95%;
left: 3%;
}
This is happening because the fadeOut is not done when the fadeIn starts. You end up with both divs visible for a short period of time, and when the fadeOut is done the first div is hidden and you see the jump.
How about something like this:
$('#players-list').fadeOut(500, function() {
$('#players-regular').fadeIn(500);
});
This way the second div is displayed only when the first one is completely hidden.
Also, decrease the animation duration a bit, it makes for better user experience ;).

Categories

Resources