dynamically Adding and removing elements based on checkbox values with DOM - javascript

I'm just trying to dynamically add to a div within a form depending on which checkboxes are checked. So, I am creating the li tag and then they are added as li elements within an ol parent element so its just a list of values. I do not know what is wrong with my code, I'm not sure how to remove the appropriate value if the relevant checkbox is unchecked, and when I uncheck and then recheck a checkbox, it keeps adding the value over and over again.
<!DOCTYPE html>
<html>
<head>
<title></title>
<style>
input {
margin: 18px;
}
#o {
list-style-type: none;
}
.u {
list-style: none;
}
</style>
</head>
<body style="width: 700px">
<div style="float: left; width: 340px; height: 250px; border: 1px solid black; padding: 20px 0 10px 20px;">
<form id="myForm">
<ul class="u">
<li><input id="showAlert1" type="checkbox" name="thing" value="laptop">laptop</li>
<li><input id="showAlert2" type="checkbox" name="thing" value="iphone">iphone</li>
</ul>
</form>
</div>
<div id="myDiv" style="float: right; width: 317px; height: 250px; border: solid black; border-width: 1px 1px 1px 0; padding: 20px 0 10px 20px;">
<ol id="o">
</ol>
</div>
<script>
document.getElementById('myForm').addEventListener('change', function () {
var a = document.getElementsByName('thing');
for (var i = 0; i < a.length; i++) {
if (a[i].checked){
createDynamicElement();
} else if (!a[i].checked){
removeDynamicElement();
}
}
function createDynamicElement(){
var node = document.createElement("LI");
node.setAttribute("id1", "Hey");
var textnode = document.createTextNode(event.target.nextSibling.data);
node.appendChild(textnode);
document.getElementById("o").appendChild(node);
}
function removeDynamicElement() {
document.querySelector("#o li").innerHTML = "";
}
});
</script>
</body>
</html>

It looks like that you are adding an event listener to the form instead of the input elements themselves. I dont think the change event will be fired when an input element in a form changes. (see: https://developer.mozilla.org/en-US/docs/Web/Events/change)
On your event listener, try targeting the input elements themselves.

} else if (!a[i].checked){
removeDynamicElement();
}
...
function removeDynamicElement() {
document.querySelector("#o li").innerHTML = "";
}
Will empty the first or all matches(not sure) but wont remove them. Instead you should give li tags a unique ID and remove them completely via something like:
for (var i = 0; i < a.length; i++) {
if (a[i].checked){
console.log(a[i])
createDynamicElement(a[i].value);
} else if (!a[i].checked){
removeDynamicElement(a[i].value);
}
}
function createDynamicElement(id){
var node = document.createElement("LI");
node.setAttribute("id", id);
var textnode = document.createTextNode(id);
node.appendChild(textnode);
console.log(node)
document.getElementById("o").appendChild(node);
}
function removeDynamicElement(id) {
var target = document.getElementById(id)
target.parentElement.removeChild(target);
}
Or you could clear the ol completely on every change and repopulate it again like:
var a = document.getElementsByName('thing');
document.getElementById("o").innerHTML = null;
for (var i = 0; i < a.length; i++) {
if (a[i].checked){
console.log(a[i])
createDynamicElement(a[i].value);
}
}
function createDynamicElement(id){
var node = document.createElement("LI");
var textnode = document.createTextNode(id);
node.appendChild(textnode);
console.log(node)
document.getElementById("o").appendChild(node);
}
Edit:
A proper FIFO solution:
var a = document.getElementsByName('thing');
for (var i = 0; i < 2; i++) {
var target = document.getElementById(a[i].value);
if (a[i].checked && !target){
createDynamicElement(a[i].value);
} else if ((!a[i].checked) && target){
removeDynamicElement(a[i].value);
}
}
function createDynamicElement(id){
var node = document.createElement("li");
node.setAttribute("id", id);
var textnode = document.createTextNode(id);
node.appendChild(textnode);
document.getElementById("o").appendChild(node);
console.log("a")
}
function removeDynamicElement(id) {
target.parentElement.removeChild(target);
}
});

Related

Copy selected <li> to another <ul> with js

function addSelected(clicked_id) {
// alert(clicked_id);
const ul = document.getElementById("sortable2");
const listItems = ul.getElementsByTagName("li");
// const ul2 = document.getElementById('slottable1');
// Loop through the NodeList object.
for (let i = 0; i <= listItems.length - 1; i++) {
if (listItems[i].className == "selectedli") console.log(listItems[i]);
//need to copy these <li> tags in another <ul> list
}
}
<ul id="slottable">
//need to copy selected <li> here and also remove class from those selected <li> before adding here
</ul>
output of the console:
<li id="pc_103" class="selectedli">B73</li>
<li id="pc_104" class="selectedli">B74</li>
I have successfully printed li which I want to copy to another ul in console logs but not able to find the right code to copy them to my another ul. I also need to remove the class 'selectedli' from the li before adding it to the ul 'slottable'.
It's done by creating dynamic tag inside slottable.
See below example:
const getChild = document.getElementById("sortable2").children;
function addSelected() {
let createUl = document.createElement("ul");
createUl.id = "slottable";
document.getElementById("tagBox").appendChild(createUl);
for (let i = 0; i < getChild.length; i++) {
if (getChild[i].className == "selectedli")
{
var createLi = document.createElement("li");
createLi.id = getChild[i].id
createLi.classList.add(getChild[i].classList);
createLi.innerHTML = getChild[i].textContent;
createUl.appendChild(createLi);
console.log(getChild[i]);
}
}
document.getElementById("sortable2").innerHTML = "";
}
ul
{
list-style: none;
}
#sortable2
{
padding: 10px;
background: red;
width: 30px;
}
#slottable
{
padding: 10px;
background: green;
width: 30px;
}
<body>
<div id="tagBox">
</div>
<ul id="sortable2">
<li id="pc_103" class="selectedli">B73</li>
<li id="pc_104" class="selectedli">B74</li>
</ul>
<input type="button" onclick="addSelected()" value="submit">
</body>
The appendChild() method should work.
Like this:
sortable2.appendChild(selectedli)
To remove classes, use selectedli.classList.remove(selectedli)
You looking for something like that?
It copied from one ul to the new ul and removes the class.
classList.remove and appendChild:
lis.map((el) => {
el.classList.remove('selectedli');
el.innerText += ' (copied and without classs slectedli)'
ul2.appendChild(el)
})
const btn = document.getElementById('transfer');
btn.addEventListener('click', () => {
// copy from slottable to sortable2
const ul = document.getElementById("slottable").children;
const ul2 = document.getElementById("sortable2");
let lis = Object.values(ul);
lis.map((el) => {
el.classList.remove('selectedli');
el.innerText += ' (copied and without classs slectedli)'
ul2.appendChild(el)
})
ul.innerHTML = '';
});
.green {
background: green;
}
.gray {
background: gray;
}
<ul id="slottable" class="gray">
<li id="pc_103" class="selectedli">B73</li>
<li id="pc_104" class="selectedli">B74</li>
</ul>
<ul id="sortable2" class="green"></ul>
<button id="transfer">click </button>

Javascript - How to limit character in a contentEditable div?

I am learning english yet. Thank you for you patience.
I created a todo list CRUD. The code is working well but I am improving even more my code.
When I press the button "plus" I create a div with the text, button delete and edit as you can see in the pic.
Task created
But when I pass a certain number of characters, it happens. (See the pic)
When the task is big, it passes to other line
How can I put a limit to it? Thank you!
My HTML code:
<body>
<header>
<h1>Lista de Tarefas</h1>
</header>
<form>
<input type="text" name="task" id="task" maxlength="34">
<button class='btnadd'><i class="fas fa-plus"></i></button>
</form>
<div class="container-tasks">
<ul class="list-task"></ul>
</div>
<script src="/js/app.js"></script>
</body>
My Javascript code:
let input = document.getElementById('task');
let btnAdd = document.querySelector(".btnadd");
let list = document.querySelector(".list-task");
// Events
btnAdd.addEventListener('click', addTask);
// Functions
function addTask(event){
event.preventDefault();
if (input.value === ''){
alert('You must type something');
return false;
} else {
addTask
}
let divTask = document.createElement("div");
divTask.classList.add("task");
let newTask = document.createElement("div");
newTask.innerText = input.value;
newTask.classList.add("new-task");
divTask.appendChild(newTask);
let btnDelete = document.createElement('button');
btnDelete.innerHTML = 'Remover';
btnDelete.classList.add("btn-remover");
divTask.appendChild(btnDelete);
let btnEdit = document.createElement('button');
btnEdit.innerHTML = 'Editar';
btnEdit.classList.add("btn-editar");
divTask.appendChild(btnEdit);
list.appendChild(divTask);
input.value = "";
btn.addEventListener('click', removeTask);
function removeTask(){
divTask.remove();
}
btnEdit.addEventListener('click', editTask);
function editTask(){
const endEdit = document.createElement('button');
endEdit.classList.add('end-edit');
endEdit.innerHTML = 'Finalizar'
divTask.appendChild(endEdit);
newTask.contentEditable = true;
newTask.focus();
btnEdit.style.display = 'none'
endEdit.addEventListener('click', endEdition);
function endEdition(){
newTask.contentEditable = false;
endEdit.remove();
btnEdit.style.display = 'block'
}
}
}
I dont think there is a definite option for that, but you can surely use keydowns count.
jQuery
$('div').on('keydown', function(event) {
$('span').text('Total chars:' + $(this).text().length);
if($(this).text().length === 100 && event.keyCode != 8) {
event.preventDefault();
}
});
div {
height: 100px;
width: 300px;
border: 1px solid grey;
outline: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div contenteditable="true"></div>
<span></span>
Vanilla JS
let maxChars = 10;
let currentChars = 0;
function checkLength(event) {
if(currentChars >= maxChars) {
alert('reached max chars');
event.preventDefault();
} else {
currentChars++;
}
}
div {
height: 100px;
width: 300px;
border: 1px solid grey;
outline: 0;
}
<div contenteditable="true" onkeypress="checkLength(event)"></div>
<span></span>

Refreshing a page returns [object CSSStyleDeclaration]

When I edit a content and saved changes, it shows [object CSSStyleDeclaration] instead of my updated content when refreshing the page.
function newElement() {
let li = document.createElement("li");
let inputvalue = document.querySelector("#myInput").value;
let savedNote = document.createTextNode(inputvalue);
li.appendChild(savedNote);
if (inputvalue === '') {
alert("Please write something")
} else {
document.querySelector("#myNotes").appendChild(li);
}
document.querySelector("#myInput").value = '';
}
function saveEdits() {
let editElement = document.querySelector("#saved-notes");
editElement = document.querySelector("#myNotes");
let userVersion = editElement.innerHTML;
userVersion = editElement.style;
localStorage.userEdits = userVersion;
document.querySelector("#update").innerHTML = "Edits saved. Refresh the page to see changed content";
}
function checkEdits() {
if (localStorage.userEdits != null)
document.querySelector("#saved-notes").innerHTML = localStorage.userEdits;
document.querySelector("#myNotes").style = localStorage.userEdits;
}
ul {
margin: 0;
padding: 0;
}
ul li {
cursor: pointer;
position: relative;
padding: 12px 8px 12px 40px;
background: #eee;
font-size: 18px;
transition: 0.2s;
}
<html>
<head>
<meta charset="utf-8">
</head>
<body onload="checkEdits()">
<div id="myDiv" class="new-note">
<textarea id="myInput"></textarea><br>
<button onclick="newElement()" class="save-button">Create</button>
<button onclick="saveEdits()" class="edit-button">Save Changes</button>
</div>
<div id="update"></div>
<div id="saved-notes"></div>
<ul id="myNotes">
</ul>
</body>
</html>
It will work if I removed userVersion = editElement.style;, but there will no CSS style. Just plain text.
I want to understand why this happens and learn from it. Thanks in advance.
It's because the Function localStorage.set can only save Strings, so it will call .toString() on the Object before it get stored and the Result of it is it's type [object CSSStyleDeclaration]. Did you tried to stringify it before you safe it?
userVersion = JSON.stringify(editElement.style);
document.querySelector("#myNotes").style = JSON.parse(localStorage.userEdits);

Javascript how to use function match()?

I have a problem with my javascript code in a word search. Let me explain:
I have an HTML page like this:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>xPression Reports</title>
<link href="index.css" rel="stylesheet" type="text/css" />
</head>
<body>
<button>Accueil</button>
<div id="myBtnContainer">
<button class="btn active" onclick="filterSelection('all')">Show all</button>
<button class="btn" onclick="filterSelection('bis')">bis</button>
<button class="btn" onclick="filterSelection('bis.xindd')">bis.xindd</button>
</div>
<div class="container">
<div class=" filterDiv bis" >bis 2017/06</div>
<div class=" filterDiv bis" >bis 2017/07</div>
<div class=" filterDiv bis.xindd" >bis.xindd 2017/06</div>
<div class=" filterDiv bis.xindd" >bis.xindd 2018/01</div>
</div>
<script src="index.js"></script>
</body>
</html>
(I simplified it)
with CSS:
body{
background-color: #d3d3d3;
}
.container {
overflow: hidden;
}
.filterDiv {
float: left;
color: black;
border: 1px solid black;
width: auto;
text-align: center;
margin: 2px;
display: none; /* Hidden by default */
}
/* The "show" class is added to the filtered elements */
.show {
display: block;
}
/* Style the buttons */
.btn {
border: none;
outline: none;
background-color: #f1f1f1;
cursor: pointer;
}
/* Add a light grey background on mouse-over */
.btn:hover {
background-color: #ddd;
}
/* Add a dark background to the active button */
.btn.active {
background-color: #666;
color: white;
}
img {
width:50px;
}
and a javascript:
filterSelection("all")
function filterSelection(c) {
var x, i;
x = document.getElementsByClassName("filterDiv");
if (c == "all") c = "";
// Add the "show" class (display:block) to the filtered elements, and remove the "show" class from the elements that are not selected
for (i = 0; i < x.length; i++) {
w3RemoveClass(x[i], "show");
if (x[i].matches(c) > -1) w3AddClass(x[i], "show");
}
}
// Show filtered elements
function w3AddClass(element, name) {
var i, arr1, arr2;
arr1 = element.className.split(" ");
//alert(arr1);
arr2 = name.split(" ");
//alert(arr2);
for (i = 0; i < arr2.length; i++) {
if (arr1.indexOf(arr2[i]) == -1) {
element.className += " " + arr2[i];
}
}
}
// Hide elements that are not selected
function w3RemoveClass(element, name) {
var i, arr1, arr2;
arr1 = element.className.split(" ");
//alert(arr1);
arr2 = name.split(" ");
//alert(arr2);
for (i = 0; i < arr2.length; i++) {
while (arr1.indexOf(arr2[i]) > -1) {
arr1.splice(arr1.indexOf(arr2[i]), 1);
}
}
element.className = arr1.join(" ");
}
// Add active class to the current control button (highlight it)
var btnContainer = document.getElementById("myBtnContainer");
var btns = btnContainer.getElementsByClassName("btn");
for (var i = 0; i < btns.length; i++) {
btns[i].addEventListener("click", function() {
var current = document.getElementsByClassName("active");
current[0].className = current[0].className.replace(" active", "");
this.className += " active";
});
}
Everything works as it should be that when I click on a button, only the frames corresponding to the button are displayed. However for 6 of them do not work for example with bis and bis.xindd, I think it because the js take only "bis" for searching and so when I click on "bis", bis and bis.xindd are displaying.
And I think the mistake comes from this function:
filterSelection("all")
function filterSelection(c) {
var x, i;
x = document.getElementsByClassName("filterDiv");
if (c == "all") c = "";
// Add the "show" class (display:block) to the filtered elements, and remove the "show" class from the elements that are not selected
for (i = 0; i < x.length; i++) {
w3RemoveClass(x[i], "show");
if (x[i].matches(c) > -1) w3AddClass(x[i], "show");
}
}
I saw information on Regex and especially the function ma * tch but I do not see how the used here
thank you in advance for your help
A working solution for your filter list would look like this: https://jsfiddle.net/jkrielaars/szagq5hm/31/
However: Periods in a class name is not a good idea.
Here is why your code is not working:
On https://developer.mozilla.org/en-US/docs/Web/API/Element/matches it states that the argument should be the element selector string. Having a period in your classname is going to mess this up.
You need to add the period to indicate you're dealing with a class, however, since you also have a period in the classname you will have this as a result: ".bis.xindd".
If you see that as a selector string, it means an element with both .bis and .xindd classes. This is not what you are aiming for.
Is you insist on using periods in your classnames, have a look at .classList.contains().
Below you can see how this differs from .matches().
var test1 = document.getElementById('test1')
var test2 = document.getElementById('test2')
console.debug('matches does not work without the class period', test1.matches('bis'));
console.debug('matches does not work without the class period', test2.matches('bis.xindd'));
console.debug('matches does work with the class period', test1.matches('.bis'));
console.debug('matches does not work with a class name with a period in it', test2.matches('.bis.xindd'));
console.debug('test1 should contain bis', test1.classList.contains("bis"));
console.debug('test 2 should contain bis.xindd', test2.classList.contains("bis.xindd"));
console.debug('test2 should not match with just "bis"', test2.classList.contains("bis"));
<div id="test1" class="filterDiv bis">bis 2017/06</div>
<div id="test2" class="filterDiv bis.xindd" >bis.xindd 2017/06</div>

Click event not working in Chrome extension options page

In my options page I generate some rows with an input number and a button, related to entries at chrome storage.
The problem is that the event listener i'm creating for the buttons doesn't work at all.
options.html
<html>
<head>
<title>Select the movie's Id</title>
<style>
body: { padding: 10px; }
.style-1 input[type="number"] {
padding: 10px;
border: solid 1px #dcdcdc;
transition: box-shadow 0.3s, border 0.3s;
width: 5em;
}
.style-1 input[type="number"]:focus,
.style-1 input[type="number"].focus {
border: solid 1px #707070;
box-shadow: 0 0 5px 1px #969696;
}
</style>
</head>
<body>
<legend style="border-bottom: solid 1px">Insert</legend>
<input type="number" name="id" id="id" value="">
<button id="save">Insert</button>
<br>
<br>
<legend style="border-bottom: solid 1px">Manage</legend>
<div id="ghost" style="display: none">
<input type="number" name="VAL">
<button name="DEL" id="" >Delete</button>
<br><br>
</div>
<script src="options.js"></script>
</body>
options.js
document.getElementById('save').addEventListener('click', save_options);
chrome.storage.sync.get('movieId', function(result){
for (var i=0; i<result.movieId.length; i++){
createRow(result.movieId[i]);
}
});
function save_options() {
var id = document.getElementById('id').value;
chrome.storage.sync.get('movieId', function(result){
var ids = result.movieId;
ids.push(id);
chrome.storage.sync.set({
'movieId': ids
}, function() {
});
location.reload();
});
}
function createRow(pos){
var newRows= document.getElementById('ghost').cloneNode(true);
newRows.id= '';
newRows.style.display= 'block';
var newRow= newRows.childNodes;
for (var i= 0; i< newRow.length; i++){
var newName= newRow[i].name;
if (newName){
newRow[i].name = newName+pos;
newRow[i].id = pos;
newRow[i].value = pos;
}
}
var insertHere= document.getElementById('ghost');
insertHere.parentNode.insertBefore(newRows,insertHere);
document.getElementById(pos).addEventListener('click', delet());
}
function loop(arrayIds){
console.log('loop');
for (var i=0; i<arrayIds.length; i++){
createRow(i);
}
}
function delet(){
console.log("this.id");
//chrome.storage.sync.remove(id);
}
With this, when I click any of the Delete buttons nothing happens.
I've tried all the combinations I can think for document.getElementById(pos).addEventListener('click', delet()); but none of them work.
document.getElementById(pos).addEventListener('click', delet());
is supposed to be
document.getElementById(pos).addEventListener('click', delet);
In your snippet you are calling delet thus result of that function is added as event listener that is undefined. If you want to bind delet as event handler, pass it to addEventListener without calling it.
EDIT
As I saw your code, you are giving same id to both input and button and when you call document.getElementById it returns input instead of button so, event is binded to input instead of button.
To fix that replace your createRow with this
function createRow(pos) {
var newRow = document.getElementById('ghost').cloneNode(true);
newRow.id= '';
newRow.style.display= 'block';
var value = newRow.querySelector("[name=VAL]");
var button = newRow.querySelector("[name=DEL]");
value.id = "VAL" + pos;
value.value = pos;
button.id = "DEL" + pos;
var insertHere= document.getElementById('ghost');
insertHere.parentNode.insertBefore(newRow, insertHere);
button.addEventListener('click', delet);
}

Categories