How to add eventlistener to dynamically created divs - javascript

How to add event delegation to dynamically created divs, to change property of the target div? I've tried several suggestions that I found for event delegation but none of them work. I think I'm making some mistakes but I don't know how to fix.
I am trying to develop a file thumbnail list interface with HTML and JavaScript. I made a method that draws thumbnails dynamically from an Array. And now I want to add some functions to manipulate the thumbnails, ex. changing border color of the item(div) when it is clicked.
First I tried loop-method to add event listeners to the divs, but it didn't work well. And now I learned that event delegation is better way to add event listeners to dynamically created elements. But the problem is that though I'v tried codes but they didn't work at all.
I think I am making some mistakes or mis-using methods but I don't know what is the problem.
JavaScript
function drawThumbnails(area, list){
var j
var createdList = []
for (j=0; j<list.length; j++){
var thmb = document.getElementById("fileThumb");
var name = document.getElementById("itemName");
var date = document.getElementById("itemDate");
var thmbimg = document.getElementById("fileThumbImage");
var thmbicon = document.getElementById("file_icon_thumb");
name.innerHTML=list[j][0];
date.innerHTML=list[j][1];
if (list[j][2] == "folder"){
thmbimg.src = "thmb_folder.png";
thmbicon.style.display = "none";
}
else {
if (list[j][2] == "img"){
thmbimg.src=getthmbimgsample();
}
else{
thmbimg.src = getThmbimg(list[j][2]);
}
thmbicon.style.display = "block";
thmbicon.src = getThmbicon(list[j][2]);
}
var cln = thmb.cloneNode(true);
cln.style.display = "block";
document.getElementById(area).append(cln);
createdList.push(cln);
}
thmbLists.push(createdList);
}
drawThumbnails("folderArea", folders);
drawThumbnails("fileArea", files);
document.getElementById("folderArea").addEventListener('click',function(e){
if(e.target && e.target.className == "fileThumb"){
e.target.style.borderColor = "#408CFF";
}
});
HTML
<body>
<div class = "contentArea" id="contentArea">
<div class = "thumbArea" id="folderArea">
<div class = "fileThumb" id="fileThumb">
<img src="icon_thumb_folder.png" class="fileThumb_normal" id="fileThumbImage">
<div class="fileName">
<img src="icon_thumb_file.png" style="width: 20px;" id="file_icon_thumb">
<div class="fileNameLine" id = "itemName">File/FolderName</div>
<div class="fileNameDate" id="itemDate">Date</div>
</div>
</div>
</div>
<div class = "contentAreaSectionHeader">
<input type="checkbox" id="chTest2" name="chTest2">
<label for="chTest2"><span>Files</span></label>
</div>
<div class = "thumbArea" id="fileArea">
</div>
</body>
CSS
.fileThumb{
width: 213px;
height: 183px;
border-radius: 2px;
border-style: solid;
border-width: 1px;
border-color: #EEEEEE;
text-align: center;
position: relative;
float:left;
margin: 18px;
display: none;
overflow: hidden;
}

Related

JavaScript delete modal onclick and decrease counter

I want to make a basic inbox function. It contains 3 messages.
So I want to make that when the user click onto the DELETE button, set the msg1's display to none, and decrease the messages value.
Here is the example code:
var x = 2;
function deleteMsg1() {
var msg1 = document.getElementsByClassName("cont");
if (confirm("Are you sure to want to delete this message?")) {
msg1[0].style.display = "none";
x = x-1;
} else {
}
}
function deleteMsg2() {
var msg2 = document.getElementsByClassName("cont2");
if (confirm("Are you sure to want to delete this message?")) {
msg2[0].style.display = "none";
x = x-1;
} else {
}
}
document.getElementById("msgcount").innerHTML = x;
.cont, .cont2 {
background-color: red;
padding: 5px;
width: 100px;
margin: 25px 0;
}
.show {
display: block;
}
<h1>There are <span id="msgcount"></span>messages</h1>
<button onclick="deleteMsg1()">Delete</button>
<div class="cont">
Some text...
</div>
<br><br>
<button onclick="deleteMsg2()">Delete</button>
<div class="cont2">
Some text...
</div>
I know this isn’t the best idea, but I guess it’s bad.
I think I should do this with one function() and try something event listener but I don't really know how to do that.
Any idea or help?
You should wrap each message's HTML in a parent element so that you can then treat each set of elements that comprise a message as a single unit and delete it all at once.
To be able to do this with a single function, you can use this to reference the element that triggered the callback function in the first place and .closest() to access the single parent wrapper.
Notes:
Do not use inline HTML event attributes, like onclick.
Separate your HTML and your JavaScript and use .addEventListener()
to bind elements to event callbacks. Even MDN recommends not using
them.
Do not use .getElementsByClassName() as it is a 25+ year old
API that has significant performance implications. Instead, use the
modern .querySelectorAll() method.
Do not use .innerHTML if you can avoid it as it has security and
performance implications. Since the text you are wanting to update
doesn't have any HTML in it anyway, .innerHTML is not warranted.
Instead, use .textContent.
// Do your event binding in JavaScript, not HTML
document.querySelectorAll("button").forEach(function(element){
element.addEventListener("click", function(){
if (confirm("Are you sure to want to delete this message?")) {
// All you need to do is delete the nearest complete
// ancestor message construct, which can be done with
// the .closest() method
this.closest(".message").remove();
updateMessageCount();
}
});
});
function updateMessageCount(){
// Set the count equal to the length of the
// collection returned by searching for all the
// messages
document.getElementById("msgcount").textContent =
document.querySelectorAll(".message").length;
}
updateMessageCount();
.cont, .cont2 {
background-color: red;
padding: 5px;
width: 100px;
margin: 25px 0;
}
.show {
display: block;
}
<h1>There are <span id="msgcount"></span> messages</h1>
<!-- By wrapping each message, you can treat all its HTML
as one single unit. -->
<div class="message">
<button>Delete</button>
<div class="cont">
Some text...
</div>
</div>
<br><br>
<div class="message">
<button>Delete</button>
<div class="cont">
Some text...
</div>
</div>
Explained
Here's a simple enough solution, you need to update the HTML manually every time you want to update the value of x. That's why I created an updateX function, it'll just take the value & update the DOM, it's quite that simple.
const updateX = (x) => {
document.getElementById("msgcount").innerHTML = x;
};
let x = 2;
const del = (className) => {
const msg = document.getElementsByClassName(className);
if (confirm("Are you sure to want to delete this message?")) {
msg[0].style.display = "none";
x--;
} else {
console.log("===");
}
updateX(x);
};
updateX(x);
.cont,
.cont2 {
background-color: red;
padding: 5px;
width: 100px;
margin: 25px 0;
}
.show {
display: block;
}
<h1>There are <span id="msgcount"></span>messages</h1>
<button onclick="del('cont')">Delete</button>
<div class="cont">
Some text...
</div>
<br/><br/>
<button onclick="del('cont2')">Delete</button>
<div class="cont2">
Some text...
</div>
My advice to you: Never declare events js inside html structure tags! As here:
<button onclick="deleteMsg1()">Delete</button>
This is a very bad practice. This has many disadvantages. And this can lead to bad consequences.
I made a solution for you with the forEach() method, without using javascript in html.
The Delete button is also removed.
let msg = document.querySelectorAll(".cont");
let btn_del = document.querySelectorAll('.btn_del');
let x = 2;
btn_del.forEach(function (btn_del_current, index) {
btn_del_current.addEventListener('click', function () {
if (confirm("Are you sure to want to delete this message?")) {
this.style.display = "none";
msg[index].style.display = "none";
x = x - 1;
document.getElementById("msgcount").innerHTML = x;
} else {}
});
});
.cont, .cont2 {
background-color: red;
padding: 5px;
width: 100px;
margin: 25px 0;
}
.show {
display: block;
}
<h1>There are <span id="msgcount"></span>messages</h1>
<button class="btn_del">Delete</button>
<div class="cont">
Some text...
</div>
<br><br>
<button class="btn_del">Delete</button>
<div class="cont">
Some text...
</div>

Giving a div a style on click

applying a class to an element only when clicked
You could make 2 different click functions. One for trap and one for the rest.
For that you need to know which ones are the other ( safe ones ). See otherDivsIds in the below code. You find the other id's using the filter function in the idArray and then loop through them ( with forEach or something else ) and add event listeners to each of them.
I would also suggest to ' swap ' the naming of the variables trapBox and trapId. Vice versa would be better
See code below
var idArray = ['one','two','three','four'];
var trapBox = idArray[Math.floor(Math.random() * idArray.length)];
var trapId= document.getElementById(trapBox);
trapId.addEventListener('click', boomClickFunction, false);
var otherDivsIds = idArray.filter(id => id !== trapBox);
otherDivsIds.forEach(id => {
safeBox = document.getElementById(id);
safeBox.addEventListener('click', safeClickFunction, false)
})
var timeoutId = window.setTimeout(ticker, 5000);
function ticker() {
document.getElementById('timesUp').innerHTML = "Time's up!";
document.body.style.backgroundColor = "black";
}
function boomClickFunction() {
this.classList.add('boom')
}
function safeClickFunction() {
this.classList.add('safe')
}
div {
width: 20px;
height: 20px;
background-color: green;
margin: 20px;
float: left;
}
.boom {
background-color: red;
}
.safe {
background-color: lightblue;
}
#timesUp {
color: white;
}
<div id='one'>
</div>
<div id='two'>
</div>
<div id='three'>
</div>
<div id='four'>
</div>
<span id="timesUp">
</span>
You can add a class to an element by using classList.add('classToBeAdded').
In your case, you could put it in your clickFunction:
trapId.classList.add('boom');

JavaScript for changing a <div> colour from a button press

I'm a little unsure why my code doesn't seem to be working when my html and JS code are within the same file. When the html and JS are separate, seems to be working fine. Can someone point out the error in my ways....I'm a newbie!!
HTML:
<div class="main">
<div class="light"></div>
<button onclick="chngCol()" id="burn">Burn!</button>
</div>
JavaScript:
chngCol() {
if(document.getElementByClass('light').style.background == "#00ffff")
{
document.getElementByClass('light').style.background = "#ffff00";
}
else if(document.getElementByClass('light').style.background == "ffff00")
{
document.getElementByClass('light').style.background = "#ff00ff";
}
else if(document.getElementByClass('light').style.background == "#ff00ff")
{
document.getElementByClass('light').style.background = "#00ffff";
}
}
CSS:
.light{
width: 50px;
height: 50px;
background-color:#00ffff;
}
All code is in the same document with the appropriate tags and however the error i'm getting in Chrome Console on the first { after calling chngCol.
There are a multitude of issues.
chngCol() { is not valid JS. Either function chngCol() { OR const chngCol = () =>
You need document.getElementsByClassName("light")[0] OR better, document.querySelector(".light")
You cannot read the background color of the element if it is not set in script first.
I think you meant to do this:
let cnt = 0;
const colors = ["#00ffff", "#ffff00", "#ff00ff"];
const chngCol = () => {
cnt++;
if (cnt >= colors.length) cnt = 0; // wrap
document.querySelector('.light').style.background = colors[cnt]; // use the array
}
document.getElementById("burn").addEventListener("click", chngCol);
.light {
width: 50px;
height: 50px;
background-color: #00ffff;
}
#burn {
width: 150px;
font-weight: 700;
}
<div class="main">
<div class="light"></div>
<button id="burn">Burn!</button>
</div>
The document.getElementByClass selector is an array selector. In order to select your element you should select the first element of the array.
Try this instead:
document.getElementsByClassName('light')[0].style.background

How can I use method on object which name I have as a string?

I'm coming back with another problem.
I have such code:
HTML
<button>first</button>
<button>second</button>
<div class="first"></div>
<div class="second"></div>
CSS
div{
margin-top: 10px;
width: 100px;
height: 100px;
border:1px solid black;
background-color: #ddd;
}
JS
const btns = document.querySelectorAll("button");
const first = document.querySelector(".first");
const second = document.querySelector(".second");
btns.forEach(btn => btn.addEventListener("click", function(){
this.classList.toggle("pressed");
let selection = this.textContent;
// selection.style.transform = "translate(100px)";
}))
https://codepen.io/ptr11dev/pen/oREymM
I'd like to create one function that'll be responsible for moving respective div to the right side by 100px - I stuck with such problem. Under "selection" I have respective name of div (stored under the same name), but simple code like
selection.style.transform = "translate(100px);"
doesn't work. I know that workaround like creating two functions and using
first.style.transform = "translate(100px);"
and
second.style.transform = "translate(100px);"
would work, but in my main code it's a bit more complicated.
I'll really appreciate any input from your side. Thanks
P.S. I'd like to use Vanilla JS.
You can find them by the class name, assuming that the button text and their class are the same.
const btns = document.querySelectorAll("button");
const first = document.querySelector(".first");
const second = document.querySelector(".second");
btns.forEach(btn => btn.addEventListener("click", function(){
this.classList.toggle("pressed");
let selection = this.textContent;
document.querySelector(`.${selection}`).style.transform = "translate(100px)";
}))
div{
margin-top: 10px;
width: 100px;
height: 100px;
border:1px solid black;
background-color: #ddd;
}
<button>first</button>
<button>second</button>
<div class="first"></div>
<div class="second"></div>
Your problem is textContext is just that TEXT not an object. This sets selection as the first element that matches the class name pulled as this.textContent;
let selection = document.getElementsByClassName(this.textContent)[0];
selection.style.transform = "translate(100px)";

HTML JS form to div comments (temporary change)

I have seen many similar problems but when I try them they end up failing. It has gotten to the point where my code is totally messed up and I need some help both cleaning it up and fixing my issue. (using chrome)
So far I have tried selecting the value of the form and putting that into a div,
I have tried to use the button as just a link to start the script so that the page doesn't reset and also many other answers found on-line, none of them are helping so I am asking for a personalised help.
function on_comment_add() {
var main = document.getElementById("div1");
var add_user_name = document.createElement("div");
var add_user_comment = document.createElement("div");
add_user_name.setAttribute("id", "add_user_name");
add_user_comment.setAttribute("id", "add_user_comment");
<!-- var node = document.createTextNode("This is new."); -->
var node_1 = document.getElementById("user_name").value;
var node_2 = document.getElementById("user_comment").value;
add_user_name.appendChild(node_1);
add_user_comment.appendChild(node_2);
var element = document.createElement("div");
element.setAttribute("id", "display_comment_div");
element.appendChild(add_user_name);
element.appendChild(add_user_comment);
main.appendChild(element);
main.innerHTML = element;
return false;
}
body {
background-color: lightGreen;
}
div.middle {
width: 80%;
margin-left: 10%;
background-color: #47e077;
height: 940px;
font-size: 10pt;
font-family: aubrey;
border: 3px solid gold;
}
.comments-form {
text-align: center;
}
#display_comment_div {
background: rgba(200, 54, 54, 0.1);
width: 80%;
margin-left: 9%;
border: 0.1px solid lightGreen;
border-radius: 25px;
}
#add_user_name {
width: 45%;
float: left;
}
#add_user_comment {
width: 45%;
display: inline-block;
float: right;
}
<div class="middle">
<div class="comments-form">
<form>
<label for="name" style="width:100px; display:inline-block;">Name</label>
<input id="user_name" type="text" placeholder="name goes here" style="width:300px; margin-left:5px;" />
<br><br>
<label for="comment" style="width:100px; display:inline-block;">Comment</label>
<textarea id="user_comment" placeholder="comment goes here" maxlength="150" style="width:300px;max-width:300px;"></textarea><br>
<button style="margin-left:310px;" onmousedown="return on_comment_add">Submit</button>
</form>
<div id="div1">
</div>
</div>
</div>
I guess what I am asking is if anyone can help me display the username and comment below the form but it seems tricky for me because I have gone through so many answers that don't work for me that I cannot think of any other ways to do it.
For clarification this code is not meant to keep the comments from the form nor is it meant to be a fully functioning site. I am just making slight modifications to some code so that I can hand it in as a college assignment.
Using onclick and pass the event inside:
<button style="margin-left:310px;" onclick="on_comment_add(event)">Submit</button>
And disable the default form submit action:
function on_comment_add(e) {
e.preventDefault()
var main = document.getElementById("div1");
var add_user_name = document.createElement("div");
var add_user_comment = document.createElement("div");
add_user_name.setAttribute("id", "add_user_name");
add_user_comment.setAttribute("id", "add_user_comment");
var node_1 = document.createElement("div");
node_1.innerHTML= document.getElementById("user_name").value;
var node_2 = document.createElement("div");
node_2.innerHTML = document.getElementById("user_comment").value;
add_user_name.appendChild(node_1);
add_user_comment.appendChild(node_2);
var element = document.createElement("div");
element.setAttribute("id", "display_comment_div");
element.appendChild(add_user_name);
element.appendChild(add_user_comment);
main.appendChild(element);
return false;
}
Workable example: https://jsfiddle.net/kingychiu/z6gnqswn/
Change type to "button" to prevent automatical form sending and add parentheses to onmousedown expression:
<button type="button" style="margin-left:310px;" onmousedown="return on_comment_add()">Submit</button>
Then change this
add_user_name.appendChild(node_1);
add_user_comment.appendChild(node_2);
to this (since node_1, node_2 are values, not elements):
add_user_name.innerHTML = node_1;
add_user_comment.innerHTML = node_2;
And remove that line
main.innerHTML = element;
above
return false;
That should work.

Categories