Unwanted background after repositioned span in Firefox - javascript

I want to move a span from one div to another.
My solution works ok in Chrome and Safari but Firefox gives me a problem - when the span is moved it has an unwanted background. The background disappears as soon as I click elsewhere on the page but I need to find a way for it not to appear in the first place. Any help is greatly appreciated.
var clckdEl = null;
var clicked = function(evt) {
var span = null;
if (clckdEl) {
if (clckdEl !== this) {
span = clckdEl.children[0];
clckdEl.removeChild(span);
this.appendChild(span);
}
clckdEl.classList.remove('selected');
clckdEl = null;
} else if (this.children.length) {
clckdEl = this;
clckdEl.classList.add('selected')
}
};
var els = document.getElementsByClassName('rect'),
el,
idx = 0;
for (idx = 0; idx < els.length; idx += 1) {
el = els[idx];
el.onclick = clicked;
}
.holder {
font: 100px Arial Unicode MS, sans-serif;
width: 2em;
height: 1em;
}
.rect {
float: left;
display: flex;
width: 1em;
height: 1em;
align-items: center;
}
.rect-blue {
background-color: blue;
}
.rect-yellow {
background-color: yellow;
}
.letter {
line-height: 1em;
padding-bottom: 15%;
margin: 0 auto;
}
.selected {
background-color: red;
}
<div class='holder'>
<div class='rect rect-blue'>
</div>
<div class='rect rect-yellow'>
<span class='letter'>X</span>
</div>
</div>

All you need is to disable user-select parameter.
Simply add this style to your holder class:
user-select: none;
-moz-user-select: none;
-webkit-user-select: none;
-ms-user-select: none;
JSFiddle

Related

Center an image in a contenteditable div

Please before tagging the question as duplicated, I tell you, I've been searching a lot and can't find a clear answer, so it may be worth it to try to get a clear one for the year we are living, 2022.
I have this contenteditable div:
<div class="input-field" placeholder="Tell me something..." contentEditable="true"></div>
I've been able to center the text and regular emojis setting the line-height in css but when I enter an image I am using as a custom emoji it does not get centered.
<img src="img/emojis/red-circle.png" class="image-emoji">
I am adding the image to the input field with event.target after I click it from another box:
inputField.innerHTML +=
"<div class='emo'>" + event.target.outerHTML + "</div>";
But the Divs get removed by innerHTML, so it blocks me from adding display:flex; align-items: center;
And if I set display:flex; align-items: center; directly on the contenteditable I get horizontal scrolling which I do not want.
Then if I set the css to be display:flex; align-items: center; flex-direction: column; on the contenteditable the image-emojis display one on each line every time, not side by side.
Please help.
EDIT:
Following the first answer advice I tested flex-wrap:wrap; on the contenteditable which also produces horizontal scrolling on long words with no spaces and flex-wrap:nowrap; does the same.
EDIT-2:
As suggested adding break-word:break-all; works partially but now I notice another problem with display: flex; It prevents the ENTER key from adding additional lines even thought <div> <br> </div> are added to the Html, any idea?
EDIT-3
Finally not using flex box, but just line-height to align text and emojis and as per Lukas below:
.input-field img {
vertical-align: middle;
}
...aligned the image.
const emojiBox = document.querySelector(".emoji-box");
const inputField = document.querySelector(".input-field");
emojiBox.addEventListener("click", (e) => {
let emoji = null;
let isImage = null;
let emojiImage = null;
let removeBR = null;
if (e.target != e.currentTarget) {
removeBR = inputField.querySelector("br");
if (removeBR) {
removeBR.outerHTML = "";
}
if (
e.target.tagName.toLowerCase() === "img" &&
e.target.classList.value.toLowerCase() === "emoji"
)
isImage = true;
if (isImage) {
emojiImage = e.target;
} else {
emoji = e.target;
}
}
if (emoji) {
inputField.innerHTML += emoji.outerHTML;
} else if (emojiImage) {
inputField.innerHTML += emojiImage.outerHTML;
}
cursorAtTheEnd();
});
function cursorAtTheEnd() {
let sel = window.getSelection();
sel.selectAllChildren(inputField);
sel.collapseToEnd();
inputField.focus();
}
html {
font-size: 62.5%;
}
.image-emoji {
max-width: 1.8rem;
max-height: 1.8rem;
border-radius: 100%;
padding: 0;
margin: 0 0.15rem 0;
}
.input-field {
display: flex;
align-items: center;
flex-wrap: wrap;
word-break: break-all;
font-size:1.6rem;
min-height: 3rem;
max-height: 20rem;
width: 40rem;
margin: 0.5rem;
padding: 1rem 6rem 1rem 4rem;
border: 2px solid #e6e6e6;
border-radius: 0.5rem;
outline: none;
overflow-y: auto;
}
[contenteditable="true"]:empty:before {
content: attr(placeholder);
pointer-events: none;
display: block; /* For Firefox */
}
.emoji-box {
display: flex;
align-items: center;
font-size:1.6rem;
background: white;
border: 0.2rem solid #eee;
border-radius: 0.5rem;
height: 3rem;
width: 40rem;
padding: 1rem 6rem 1rem 4rem;
margin: 0.5rem;
color: #183153;
cursor: pointer;
overflow-y: auto;
}
<div class="emoji-box">
<span>🙂</span>
<img src="https://upload.wikimedia.org/wikipedia/commons/0/02/Red_Circle%28small%29.svg" class="image-emoji">
</div>
<div class="input-field" placeholder="Tell me something..." contentEditable="true"></div>
try this
const emojiBox = document.querySelector(".emoji-box");
const inputField = document.querySelector(".input-field");
let lastKeyEnter = false;
function removeBR() {
let removeBR = inputField.querySelector("br:last-of-type");
if (
removeBR &&
removeBR.previousElementSibling &&
removeBR.previousElementSibling.tagName === "BR"
) {
removeBR.remove();
}
}
inputField.onkeydown = (e) => {
if (e.keyCode === 13) {
lastKeyEnter = true;
} else if (lastKeyEnter) {
lastKeyEnter = false;
}
};
emojiBox.addEventListener("click", (e) => {
let emoji = null;
let isImage = null;
let emojiImage = null;
if (e.target != e.currentTarget) {
if (
e.target.tagName.toLowerCase() === "img" &&
e.target.classList.value.toLowerCase() === "emoji"
)
isImage = true;
if (isImage) {
emojiImage = e.target;
} else {
emoji = e.target;
}
}
let lastChild = inputField.lastChild;
if (
lastChild &&
lastChild.previousSibling &&
lastChild.previousSibling.tagName === undefined
) {
lastChild.tagName === "BR" ? lastChild.remove() : "";
}
if (emoji && emoji.tagName === "SPAN") {
lastKeyEnter ? removeBR() : "";
lastKeyEnter = false;
inputField.innerHTML += emoji.innerHTML;
} else if (emoji && emoji.tagName === "IMG") {
lastKeyEnter ? removeBR() : "";
lastKeyEnter = false;
inputField.innerHTML += emoji.outerHTML;
} else if (emojiImage) {
lastKeyEnter ? removeBR() : "";
lastKeyEnter = false;
inputField.innerHTML += emojiImage.outerHTML;
}
cursorAtTheEnd();
});
function cursorAtTheEnd() {
let sel = window.getSelection();
sel.selectAllChildren(inputField);
sel.collapseToEnd();
inputField.focus();
}
html {
font-size: 62.5%;
}
.image-emoji {
max-width: 1.8rem;
max-height: 1.8rem;
border-radius: 100%;
padding: 0;
margin: 0 0.15rem 0;
}
.input-field {
word-break: break-all;
font-size:1.6rem;
min-height: 3rem;
max-height: 20rem;
width: 40rem;
margin: 0.5rem;
padding: 1rem 6rem 1rem 4rem;
border: 2px solid #e6e6e6;
border-radius: 0.5rem;
outline: none;
overflow-y: auto;
display: inline-block;
line-height: 2rem;
}
.input-field img {
vertical-align: text-bottom;
}
[contenteditable="true"]:empty:before {
content: attr(placeholder);
pointer-events: none;
display: block; /* For Firefox */
}
.emoji-box {
display: flex;
align-items: center;
font-size:1.6rem;
background: white;
border: 0.2rem solid #eee;
border-radius: 0.5rem;
height: 3rem;
width: 40rem;
padding: 1rem 6rem 1rem 4rem;
margin: 0.5rem;
color: #183153;
cursor: pointer;
overflow-y: auto;
}
<div class="emoji-box">
<span>🙂</span>
<img src="https://upload.wikimedia.org/wikipedia/commons/0/02/Red_Circle%28small%29.svg" class="image-emoji">
</div>
<div class="input-field" placeholder="Tell me something..." contentEditable="true"></div>

Move li items on click between lists does not work properly

I have to lists and am using a jquery function to move items from one list (id="columns")to the other (id="columns1") when I click on the items in the first list (id="columns").
That part of it works fine, but when I add a new item in the first list (id="columns"), the previously moved items show up in the first list (id="columns").
Is there a way to prevent the moved items to show up in the first list? Is there a way to do it with vanilla js or do I need to use jquery?
This is all part of an to do list app I am creating where you could add tasks, remove them, click on the task to consider it a finished task, etc.
// Create a "close" button and append it to each list item
var myNodelist = document.getElementsByTagName("LI");
var i;
for (i = 0; i < myNodelist.length; i++) {
var span = document.createElement("SPAN");
var txt = document.createTextNode("\u00D7");
span.className = "close";
span.appendChild(txt);
myNodelist[i].appendChild(span);
}
// Click on a close button to hide the current list item
function closeEvent() {
var close = document.getElementsByClassName("close");
for (var i = 0; i < close.length; i++) {
close[i].onclick = function () {
var div = this.parentElement;
div.style.display = "none";
renderGraph();
}
}
}
// Add a "checked" symbol when clicking on a list item
var list = document.querySelector('ul');
list.addEventListener('click', function (ev) {
if (ev.target.tagName !== 'LI') return;
ev.target.classList.toggle('checked');
renderGraph();
}, false);
// Create a new list item when clicking on the "Add" button
function newElement() {
var li = document.createElement("li");
li.className = "column";
li.draggable = "true";
// order according to time
li.setAttribute("data-time", document.getElementById("myInput1").value);
// order according to time
var inputValue = document.getElementById("myInput").value;
var inputValue1 = document.getElementById("myInput1").value;
var tine = document.getElementById("myInput1");
tine.dateTime = "6:00"
// inputValue2.datetime = "6:00";
var tt = document.createTextNode(inputValue1 + " - ");
li.appendChild(tt);
var t = document.createTextNode(inputValue);
li.appendChild(t);
if (inputValue === '') {
alert("You must write a task!");
} else {
document.getElementById("columns").appendChild(li);
// order according to time start
setTimeout(function () {
var sortItems = document.querySelectorAll("[data-time]");
var elemArray = Array.from(sortItems);
elemArray.sort(function (a, b) {
if (a.getAttribute('data-time') < b.getAttribute('data-time')) { return -1 } else { return 1 }
});
//
document.getElementById("columns").innerHTML = "";
elemArray.forEach(appendFunction);
function appendFunction(item, index) {
document.getElementById("columns").innerHTML += item.outerHTML;
}
afterUpdate();
});
// order according to time end
}
document.getElementById("myInput").value = "";
document.getElementById("myInput1").value = "";
var span = document.createElement("SPAN");
var txt = document.createTextNode("\u00D7");
span.className = "close";
span.appendChild(txt);
li.appendChild(span);
for (i = 0; i < close.length; i++) {
close[i].onclick = function () {
var div = this.parentElement;
div.style.display = "none";
}
}
}
// Add tasks by pressing enter
// Get the input field
var input = document.getElementById("myInput");
// Execute a function when the user releases a key on the keyboard
input.addEventListener("keyup", function (event) {
// Number 13 is the "Enter" key on the keyboard
if (event.keyCode === 13) {
// Cancel the default action, if needed
event.preventDefault();
// Trigger the button element with a click
document.getElementById("myBtn").click();
}
});
// //
// //
var btn = document.querySelector('.add');
var remove = document.querySelector('.column');
function dragStart(e) {
this.style.opacity = '0.4';
dragSrcEl = this;
e.dataTransfer.effectAllowed = 'move';
e.dataTransfer.setData('text/html', this.innerHTML);
};
function dragEnter(e) {
this.classList.add('over');
}
function dragLeave(e) {
e.stopPropagation();
this.classList.remove('over');
}
function dragOver(e) {
e.preventDefault();
e.dataTransfer.dropEffect = 'move';
return false;
}
function dragDrop(e) {
if (dragSrcEl != this) {
dragSrcEl.innerHTML = this.innerHTML;
this.innerHTML = e.dataTransfer.getData('text/html');
closeEvent();
}
return false;
}
function dragEnd(e) {
var listItems = document.querySelectorAll('.column');
[].forEach.call(listItems, function (item) {
item.classList.remove('over');
});
this.style.opacity = '1';
}
function addEventsDragAndDrop(el) {
el.addEventListener('dragstart', dragStart, false);
el.addEventListener('dragenter', dragEnter, false);
el.addEventListener('dragover', dragOver, false);
el.addEventListener('dragleave', dragLeave, false);
el.addEventListener('drop', dragDrop, false);
el.addEventListener('dragend', dragEnd, false);
}
function addDragAndDrop() {
var listItems = document.querySelectorAll('.column');
listItems.forEach(addEventsDragAndDrop);
}
afterUpdate();
function afterUpdate() {
closeEvent();
addDragAndDrop();
renderGraph();
}
function addNewItem() {
var newItem = document.querySelector('.input').value;
if (newItem != '') {
document.querySelector('.input').value = '';
var li = document.createElement('li');
var attr = document.createAttribute('column');
var ul = document.querySelector('ul');
li.className = 'column';
attr.value = 'true';
li.setAttributeNode(attr);
li.appendChild(document.createTextNode(newItem));
ul.appendChild(li);
addEventsDragAndDrop(li);
}
}
#myInput1 {
width: 180px;
height: 36px;
margin-right: 10px;
/* margin-left: -40px; */
/* padding: 10px; */
/* color: red; */
/* box-sizing: border-box; */
/* background-color: blue; */
/* display: inline-block; */
}
[draggable] {
-moz-user-select: none;
-khtml-user-select: none;
-webkit-user-select: none;
user-select: none;
/* Required to make elements draggable in old WebKit */
-khtml-user-drag: element;
-webkit-user-drag: element;
}
/* Include the padding and border in an element's total width and height */
* {
box-sizing: border-box;
font-family: 'Josefin Sans', sans-serif;
}
p {
font-weight: 300;
}
h1 {
font-weight: 300;
}
#x-text {
color: #f97350;
font-size: 1.5rem;
}
::placeholder{
color: #777d71;
}
#myInput1:before {
content:'Time:';
margin-right:.6em;
color: #777d71;
}
/* Remove margins and padding from the list */
ul {
margin: 0;
padding: 0;
list-style: none;
}
/* Style the list items */
ul li {
cursor: pointer;
position: relative;
padding: 12px 8px 12px 40px;
background: #f7f6e7;
font-size: 18px;
transition: 0.2s;
color: rgb(94, 91, 91);
/* make the list items unselectable */
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
/* Set all odd list items to a different color (zebra-stripes) */
ul li:nth-child(odd) {
background: #dfddc5;
}
/* Darker background-color on hover */
ul li:hover {
background: rgb(207, 205, 205);
}
/* When clicked on, add a background color and strike out text */
ul li.checked {
background: #888;
color: #fff;
text-decoration: line-through;
}
/* Add a "checked" mark when clicked on */
ul li.checked::before {
content: '';
position: absolute;
border-color: #fff;
border-style: solid;
border-width: 0 2px 2px 0;
top: 10px;
left: 16px;
transform: rotate(45deg);
height: 15px;
width: 7px;
}
/* Style the close button */
.close {
position: absolute;
right: 0;
top: 0;
padding: 12px 16px 12px 16px;
color: #f97350;
/* font-size: 1.5rem; */
}
.close:hover {
background-color: #f97350;
color: white;
}
/* Style the header */
.header {
background-color: #777d71;
padding: 30px 40px;
color: white;
text-align: center;
}
/* Clear floats after the header */
.header:after {
content: "";
display: table;
clear: both;
}
/* Style the input */
input {
margin: 0;
border: none;
border-radius: 0;
width: 75%;
padding: 10px;
float: left;
font-size: 16px;
}
/* Style the "Add" button */
/* .addBtn {
padding: 10px;
width: 25%;
background: #d9d9d9;
color: #555;
float: left;
text-align: center;
font-size: 16px;
cursor: pointer;
transition: 0.3s;
border-radius: 0;
} */
/* .addBtn:hover {
background-color: #bbb;
} */
#finished-tasks {
/* box-sizing: border-box; */
padding: 20px;
display: grid;
align-content: center;
justify-content: center;
background-color:#bbd38b ;
color: white;
margin-bottom: -20px;
margin-top: 40px;
}
#finished-tasks-p {
/* box-sizing: border-box; */
padding: 20px;
display: grid;
align-content: center;
justify-content: center;
background-color:#bbd38b ;
color: white;
margin-bottom: 0;
margin-top: 0;
}
/* #myChart{
margin-top: 50px;
width: 50vw;
height: 100px;
padding-left: 200px;
padding-right: 200px;
} */
/* canvas{
width:1000px !important;
height:auto !important;
margin: auto;
} */
#media only screen and (max-width: 855px){
input {
margin: 10px;
}
#myInput {
display: grid;
width:70vw;
/* align-content: center; */
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="myDIV" class="header">
<h1>My Daily Tasks</h1>
<p>Add a time and task then press enter. When finished task click on task bar</p>
<p>To delete task click on <span id="x-text">x</span> in the corner of task bar</p>
<input type="time" id="myInput1" value="06:00">
<input name="text" type="text" id="myInput" placeholder="My task...">
<span onclick="newElement()" class="addBtn" id="myBtn"></span>
</div>
<ul id="columns">
<!-- <li draggable="true" class="column">test</li>
<li draggable="true" class="column">test1</li> -->
<!-- <li class="column" draggable="true">w</li>
<li class="column" draggable="true">ff</li>
<li class="column" draggable="true">uuu</li> -->
</ul>
<ul id="columns2">
<h1 id="finished-tasks">finished Tasks</h1>
<p id="finished-tasks-p">Finished tasks would show up here once you have clicked on the task</p>
</ul>
<!-- adding a graph -->
<canvas id="myChart" width="400" height="400"></canvas>
<script src="/graph.js"></script>
<script src="/app.js"></script>
<!--
<div id="element"></div>
<script>
document.getElementById('element').innerHTML = 'Hi';
</script>-->
<script>
$("#columns").on('click', 'li', function () {
$(this).appendTo('#columns2');
});
$("#listC").on('click', 'li', function () {
$(this).appendTo('#listB');
});
</script>
Change your setTimeout call to this:
setTimeout(function () {
var sortItems = Array.from(document.querySelectorAll("[data-time]"))
.filter((item) => !item.classList.contains('checked'))
.sort(function (a, b) {
if (a.getAttribute('data-time') < b.getAttribute('data-time')) { return -1 } else { return 1 }
});
document.getElementById("columns").innerHTML = "";
sortItems.forEach(appendFunction);
function appendFunction(item, index) {
document.getElementById("columns").innerHTML += item.outerHTML;
}
afterUpdate();
});
The key passage is here:
.filter((item) => !item.classList.contains('checked'))
Before, you were selecting all of the items, even the ones that were already checked. This filters those out.
Here's a working JSFiddle you can experiment with. I had to add an event listener to the addBtn to get it to work on there, but your original script is fine when running from my local machine.

Why isn't the string.length updating?

This is my first piece of code js done on my own. I am trying to have an input field to add items to the list and then if you press the button to generate code it will collect all the checked items and copy the text into another div.
Basically, my question is around two variables:
const list = listUl.children;
const listCopy = listUl.querySelectorAll('span');
Let say I have 4 items in the list. If I add a new item to the list I can see the list.length add this new item, it changes from 4 to 5.
But it doesn't happen with listCopy.length the value keeps being 4.
Why is it happening if lstCopy is inside of list?
How can I have listCopy updated too?
Here is my code:
const addItemInput = document.querySelector('.addItemInput');
const addItemButton = document.querySelector('.addItemButton');
const copyText = document.querySelector('.generateCode');
const listUl = document.querySelector('.list');
const list = listUl.children;
const listCopy = listUl.querySelectorAll('span');
const clonedCode = document.querySelector('.code p');
//FUNCTION: Generate value/items = Draggable, Checkbox, Remove button
const attachItemListButton = (item) => {
//Draggable
item.draggable = "true";
item.classList.add("list--item");
//Checkbox
let checkbox = document.createElement('input');
checkbox.type = 'checkbox';
checkbox.className = 'checkbox';
checkbox.name = "chkboxName1";
checkbox.value = "value";
checkbox.id = "id";
item.insertBefore(checkbox, item.childNodes[0] || null);
//Remove button
let remove = document.createElement('button');
remove.className = 'remove';
remove.textContent = 'x';
item.appendChild(remove);
};
for (let i = 0; i < list.length; i += 1) {
attachItemListButton(list[i]);
}
//Cloning code if there are checked items
copyText.addEventListener('click', () => {
let copyTextFromList = "";
for (let i = 0; i < listCopy.length; i += 1) {
if (listCopy[i].parentNode.querySelector("input:checked")) {
copyTextFromList += listCopy[i].textContent + ',';
}
}
clonedCode.innerHTML = copyTextFromList;
});
//Add item from the input field to the list
addItemButton.addEventListener('click', () => {
let li = document.createElement('li');
let span = document.createElement('span');
span.textContent = addItemInput.value;
listUl.appendChild(li);
li.appendChild(span);
attachItemListButton(li);
addItemInput.value = '';
});
//FUNCTION: Remove button
listUl.addEventListener('click', (event) => {
if (event.target.tagName == 'BUTTON') {
if (event.target.className == 'remove') {
let li = event.target.parentNode;
let ul = li.parentNode;
ul.removeChild(li);
}
}
});
/* Google fonts */
#import url('https://fonts.googleapis.com/css?family=Heebo:300,400,700');
/* Root */
:root {
--color-white: #fff;
--color-black: #2D3142;
--color-black-2: #0E1116;
--color-gray: #CEE5F2;
--color-gray-2: #ACCBE1;
--color-gray-3: #CEE5F2;
--color-green: #439775;
--color-blue: #4686CC;
}
body {
font-family: 'Heebo', sans-serif;
font-weight: 400;
font-size: 16px;
color: black;
}
h2 {
font-weight: 700;
font-size: 1.5rem;
}
h3 {
font-weight: 700;
font-size: 1.25rem;
}
button {
background: var(--color-blue);
padding: 5px 10px;
border-radius: 5px;
color: var(--color-white);
}
[draggable] {
-moz-user-select: none;
-khtml-user-select: none;
-webkit-user-select: none;
user-select: none;
-khtml-user-drag: element;
-webkit-user-drag: element;
}
ul.list {
list-style-type: none;
padding: 0;
max-width: 300px;
}
.list button {
background: var(--color-black);
}
.list--item {
display: flex;
justify-content: space-between;
align-items: center;
width: auto;
margin: 5px auto;
padding: 5px;
cursor: move;
background: var(--color-gray);
border-radius: 5px;
}
.list--item.draggingElement {
opacity: 0.4;
}
.list--item.over {
border-top: 3px solid var(--color-green);
}
button.remove {
margin: auto 0 auto auto;
}
input#id {
margin: auto 5px auto 0;
}
.button-wrapper {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
max-width: 300px;
}
.button-wrapper .addItemInput {
width: 63%;
border-radius: 5px 0 0 5px;
}
.button-wrapper .addItemButton {
width: 35%;
border-radius: 0 5px 5px 0;
}
.button-wrapper .generateCode {
width: 100%;
background: var(--color-green);
margin-top: 5px;
}
.code p {
background: var(--color-gray); padding: 5px;
border: 1px solid var(--color-gray-2);
min-height: 20px;
font-weight: 300;
}
<ul class="list">
<li><span>Header</span></li>
<li><span>Hero</span></li>
<li><span>Intro</span></li>
<li><span>Footer</span></li>
</ul>
<div class="button-wrapper">
<input type="text" class="addItemInput" placeholder="Item description">
<button class="addItemButton">Add item</button>
<button class="generateCode">Generate code</button>
</div>
<div class="code">
<h2>Code</h2>
<p></p>
</div>
There are two variants of NodeList, live and non-live ones. querySelectorAll returns a static NodeList that is not live. .children returns a live one (technically it returns an HTMLCollection but you can ignore this distinction for now).
To make listCopy be live as well, you could use listUl.getElementsByTagName('span')…
To select elements by their classes, use getElementsByClassName. There is no way (that I know of) to get a live collection with CSS or XPath (i.e. more complex) queries, though.
The problem is that const listCopy = listUl.querySelectorAll('span'); is initiated with the span array from the beginning.
In order to get updated list
const listCopy = listUl.querySelectorAll('span'); will be let listCopy = listUl.querySelectorAll('span'); and in your function
//Cloning code if there are checked items
copyText.addEventListener('click', () => {
// add the following line - in this way you will select the span from the updated list
listCopy = listUl.querySelectorAll('span');
let copyTextFromList = "";
for (let i = 0; i < listCopy.length; i += 1) {
if (listCopy[i].parentNode.querySelector("input:checked")) {
copyTextFromList += listCopy[i].textContent + ',';
}
}
clonedCode.innerHTML = copyTextFromList;
});
if you want to use querySelectorAll, this maybe help. with this every time you check the length it recalculates and returns the value. Symbol.iterator helps you to manipulate for...of loops.
const addItemInput = document.querySelector('.addItemInput');
const addItemButton = document.querySelector('.addItemButton');
const copyText = document.querySelector('.generateCode');
const listUl = document.querySelector('.list');
const list = listUl.children;
const listCopy = {
get length() {
return listUl.querySelectorAll('span').length
},
*[Symbol.iterator]() {
let i = 0;
const l = listUl.querySelectorAll('span');
while( i < l.length ) {
yield l[i];
i++;
}
}
};
const clonedCode = document.querySelector('.code p');
//FUNCTION: Generate value/items = Draggable, Checkbox, Remove button
const attachItemListButton = (item) => {
//Draggable
item.draggable = "true";
item.classList.add("list--item");
//Checkbox
let checkbox = document.createElement('input');
checkbox.type = 'checkbox';
checkbox.className = 'checkbox';
checkbox.name = "chkboxName1";
checkbox.value = "value";
checkbox.id = "id";
item.insertBefore(checkbox, item.childNodes[0] || null);
//Remove button
let remove = document.createElement('button');
remove.className = 'remove';
remove.textContent = 'x';
item.appendChild(remove);
};
for (let i = 0; i < list.length; i += 1) {
attachItemListButton(list[i]);
}
//Cloning code if there are checked items
copyText.addEventListener('click', () => {
let copyTextFromList = "";
for (let item of listCopy) {
if (item.parentNode.querySelector("input:checked")) {
copyTextFromList += item.textContent + ',';
}
}
clonedCode.innerHTML = copyTextFromList;
});
//Add item from the input field to the list
addItemButton.addEventListener('click', () => {
let li = document.createElement('li');
let span = document.createElement('span');
span.textContent = addItemInput.value;
listUl.appendChild(li);
li.appendChild(span);
attachItemListButton(li);
addItemInput.value = '';
});
//FUNCTION: Remove button
listUl.addEventListener('click', (event) => {
if (event.target.tagName == 'BUTTON') {
if (event.target.className == 'remove') {
let li = event.target.parentNode;
let ul = li.parentNode;
ul.removeChild(li);
}
}
});
/* Google fonts */
#import url('https://fonts.googleapis.com/css?family=Heebo:300,400,700');
/* Root */
:root {
--color-white: #fff;
--color-black: #2D3142;
--color-black-2: #0E1116;
--color-gray: #CEE5F2;
--color-gray-2: #ACCBE1;
--color-gray-3: #CEE5F2;
--color-green: #439775;
--color-blue: #4686CC;
}
body {
font-family: 'Heebo', sans-serif;
font-weight: 400;
font-size: 16px;
color: black;
}
h2 {
font-weight: 700;
font-size: 1.5rem;
}
h3 {
font-weight: 700;
font-size: 1.25rem;
}
button {
background: var(--color-blue);
padding: 5px 10px;
border-radius: 5px;
color: var(--color-white);
}
[draggable] {
-moz-user-select: none;
-khtml-user-select: none;
-webkit-user-select: none;
user-select: none;
-khtml-user-drag: element;
-webkit-user-drag: element;
}
ul.list {
list-style-type: none;
padding: 0;
max-width: 300px;
}
.list button {
background: var(--color-black);
}
.list--item {
display: flex;
justify-content: space-between;
align-items: center;
width: auto;
margin: 5px auto;
padding: 5px;
cursor: move;
background: var(--color-gray);
border-radius: 5px;
}
.list--item.draggingElement {
opacity: 0.4;
}
.list--item.over {
border-top: 3px solid var(--color-green);
}
button.remove {
margin: auto 0 auto auto;
}
input#id {
margin: auto 5px auto 0;
}
.button-wrapper {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
max-width: 300px;
}
.button-wrapper .addItemInput {
width: 63%;
border-radius: 5px 0 0 5px;
}
.button-wrapper .addItemButton {
width: 35%;
border-radius: 0 5px 5px 0;
}
.button-wrapper .generateCode {
width: 100%;
background: var(--color-green);
margin-top: 5px;
}
.code p {
background: var(--color-gray); padding: 5px;
border: 1px solid var(--color-gray-2);
min-height: 20px;
font-weight: 300;
}
<ul class="list">
<li><span>Header</span></li>
<li><span>Hero</span></li>
<li><span>Intro</span></li>
<li><span>Footer</span></li>
</ul>
<div class="button-wrapper">
<input type="text" class="addItemInput" placeholder="Item description">
<button class="addItemButton">Add item</button>
<button class="generateCode">Generate code</button>
</div>
<div class="code">
<h2>Code</h2>
<p></p>
</div>

Targeting and Manipulating setInterval Timer Value [duplicate]

This question already has answers here:
Changing the interval of SetInterval while it's running
(17 answers)
Closed 5 years ago.
I have a simple function that changes the color of a square from red to blue every 2 seconds. I need the speed of the setInterval to reflect the value in the counter. I can't seem to figure out how to target the time value in the setInterval. No jQuery, thanks. Heres a demo of my code.
function Tempo() {
var tempoVal = document.getElementById("tempo-value");
var tempoBtn = tempoVal.querySelectorAll("[data-btn]");
var tempoNum = tempoVal.querySelector("[data-value]");
for (var i = 0; i < tempoBtn.length; i++) {
tempoBtn[i].onclick = function() {
if (this.getAttribute("data-btn") == "-") {
tempoNum.innerHTML = parseFloat(tempoNum.innerHTML) - 1;
} else if (this.getAttribute("data-btn") == "+") {
tempoNum.innerHTML = parseFloat(tempoNum.innerHTML) + 1;
}
};
}
}
let myTempo = new Tempo(document.getElementById("tempo-value"));
//BLOCK
setInterval(function() {
var block = document.getElementById("block");
block.classList.toggle("color");
}, 2000);
#tempo-value {
font-family: Sans-Serif;
font-size: 24px;
text-align: center;
padding: 16px;
user-select: none;
}
.btn {
display: inline-flex;
cursor: pointer;
}
.value {
display: inline-block;
width: 40px;
text-align: right;
}
#block {
width: 100px;
height: 100px;
background-color: red;
margin: 0 auto;
}
#block.color {
background-color: blue;
}
<div id="tempo-value">
<div data-btn='-' class="btn">&#9669</div>
<div data-value class="value">1</div><span> second(s)</span>
<div data-btn='+' class="btn">&#9659</div>
</div>
<div id="block"></div>
You can assign setTimeout / setInterval to a variable. Then use clearTimeout / clearInterval to remove it when necessary. Then set new one again. See code below:
function Tempo() {
var toggle = function() {
document.getElementById('block')
.classList.toggle('color');
}
var t = 1000;
var blink = setInterval(toggle, t);
var tempoVal = document.getElementById('tempo-value');
var tempoBtn = tempoVal.querySelectorAll('[data-btn]');
var tempoNum = tempoVal.querySelector('[data-value]');
for (var i = 0; i < tempoBtn.length; i++) {
tempoBtn[i].onclick = function() {
clearInterval(blink);
tempoNum.innerHTML = +
this.getAttribute('data-btn') +
+tempoNum.innerHTML;
t = 1000 * tempoNum.innerHTML;
blink = setInterval(toggle, t);
}
}
};
let myTempo = new Tempo(document.getElementById('tempo-value'));
body,
html {
height: 100%;
}
#tempo-value {
font-family: Sans-Serif;
font-size: 24px;
text-align: center;
padding: 16px;
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.btn {
display: inline-flex;
cursor: pointer;
}
.value {
display: inline-block;
width: 40px;
text-align: right;
}
#block {
width: 100px;
height: 100px;
background-color: red;
margin: 0 auto;
}
#block.color {
background-color: blue;
}
<div id="tempo-value">
<div data-btn='-1' class="btn">&#9669</div>
<div data-value class="value">1</div><span> second(s)</span>
<div data-btn='+1' class="btn">&#9659</div>
</div>
<div id="block"></div>

Javascript two window.onlick functions clashing

I am using a couple of drop down boxes with the below scripts, everything works except for clicking outside of the window, which will only work on the last button, I know there is a way to seperate the window.onclick functions but I'm not sure how, have tried a few things but can't find much information on this.
Any help would be much appreciated!
<script>
/* When the user clicks on the button,
toggle between hiding and showing the dropdown content */
function myFunction20() {
document.getElementById("myDropdown20").classList.toggle("show");
}
// Close the dropdown if the user clicks outside of it
window.onclick = function(event20) {
if (!event.target.matches('.dropbtn20')) {
var dropdowns20 = document.getElementsByClassName("dropdown-content20");
var i;
for (i = 0; i < dropdowns20.length; i++) {
var openDropdown20 = dropdowns20[i];
if (openDropdown20.classList.contains('show')) {
openDropdown20.classList.remove('show');
}
}
}
}
</script>
<script>
/* When the user clicks on the button,
toggle between hiding and showing the dropdown content */
function myFunction21() {
document.getElementById("myDropdown21").classList.toggle("show");
}
// Close the dropdown if the user clicks outside of it
window.onclick = function(event21) {
if (!event.target.matches('.dropbtn21')) {
var dropdowns21 = document.getElementsByClassName("dropdown-content21");
var i;
for (i = 0; i < dropdowns21.length; i++) {
var openDropdown21 = dropdowns21[i];
if (openDropdown21.classList.contains('show')) {
openDropdown21.classList.remove('show');
}
}
}
}
</script>
the one on the right will close when clicking outside the button and dropdown but the one on the left will not.. https://jsfiddle.net/c94gLhqm/
The result of the comments chat above. Adding here for clarity. Introduced onblur in the HTML
/* When the user clicks on the button,
toggle between hiding and showing the dropdown content */
function myFunction() {
document.getElementById("myDropdown").classList.toggle("show");
}
function myFunction2() {
document.getElementById("myDropdown2").classList.toggle("show");
}
.dropbtn {
background-color: #85c445;
color: white;
padding: 16px;
font-size: 16px;
border: none;
cursor: pointer;
width: 170px;
}
.dropbtn:hover, .dropbtn:focus {
background-color: #3e8e41;
}
.dropdown {
position: relative;
display: inline-block;
z-index: 6
}
.dropdown-content {
display: none;
position: absolute;
background-color: #f9f9f9;
min-width: 160px;
overflow: auto;
box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
}
.dropdown-content a {
color: black;
font-size: 16px;
padding: 12px 16px;
text-decoration: none;
display: block;
}
.dropdown a:hover {background-color: #f1f1f1}
.show {display:block;}
.dropbtn2 {
background-color: #85c445;
color: white;
padding: 16px;
font-size: 16px;
border: none;
cursor: pointer;
width: 170px;
}
.dropbtn2:hover, .dropbtn2:focus {
background-color: #3e8e41;
}
.dropdown2 {
position: relative;
display: inline-block;
z-index: 6
}
.dropdown-content2 {
display: none;
position: absolute;
background-color: #f9f9f9;
min-width: 160px;
overflow: auto;
box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
}
.dropdown-content2 a {
color: black;
font-size: 16px;
padding: 12px 16px;
text-decoration: none;
display: block;
}
.dropdown2 a:hover {background-color: #f1f1f1}
.show {display:block;}
<div class="dropdown">
<button onblur="myFunction()" onclick="myFunction()" class="dropbtn">Example</button>
<div id="myDropdown" class="dropdown-content">
1
2
3
4
5
</div>
</div>
<div class="dropdown2">
<button onblur="myFunction2()" onclick="myFunction2()" class="dropbtn2">Example</button>
<div id="myDropdown2" class="dropdown-content2">
1
2
3
4
5
</div>
</div>
I think change your once window onclick method
<script>
/* When the user clicks on the button,
toggle between hiding and showing the dropdown content */
function myFunction20() {
document.getElementById("myDropdown20").classList.toggle("show");
}
/* When the user clicks on the button,
toggle between hiding and showing the dropdown content */
function myFunction21() {
document.getElementById("myDropdown21").classList.toggle("show");
}
// Close the dropdown if the user clicks outside of it
window.onclick = function(event20) {
if (!event.target.matches('.dropbtn20')) {
var dropdowns20 = document.getElementsByClassName("dropdown-content20");
var i;
for (i = 0; i < dropdowns20.length; i++) {
var openDropdown20 = dropdowns20[i];
if (openDropdown20.classList.contains('show')) {
openDropdown20.classList.remove('show');
}
}
} else if (!event.target.matches('.dropbtn21')) {
var dropdowns21 = document.getElementsByClassName("dropdown-content21");
var i;
for (i = 0; i < dropdowns21.length; i++) {
var openDropdown21 = dropdowns21[i];
if (openDropdown21.classList.contains('show')) {
openDropdown21.classList.remove('show');
}
}
}
}
</script>

Categories

Resources