I have a for loop which is responsible for opening a custom modal. The opening button is defined as multiple elements with the class of .col-sm-4
Here are the code:
// Get the modal
var modal = document.getElementById('myModal');
// Get the opening trigger
var btn = document.getElementsByClassName('col-sm-4');
for (var i = 0; i < btn.length; i++) {
// Open modal by clicking the order display
btn[i].onclick = function(event) {
// Check to see if target is the parent element
if(event.target.tagName.toLowerCase() === 'div') {
modal.style.display = 'block';
document.getElementsByClassName('col-sm-4');
var idDIVOrderParent = btn[0].id;
// Having trouble grabbing the correct ID
console.log('The order ID is ' + idDIVOrderParent);
// extract orderId9999 from string like parentOrderId9999
var idDIVOrder = 'o' + idDIVOrderParent.substr(7);
console.log('The modal is currently displaying ' + idDIVOrder);
document.getElementById(idDIVOrder).style.display = 'block';
} else {
// console.log('Child element clicked!');
}
}
}
Where it says var idDIVOrderParent = btn[0].id is where I'm having difficulties. How can I grab the ID of the correct .col-sm-4 which triggered the modal?
This should do the trick.
var modal = document.getElementById('myModal');
var btn = document.getElementsByClassName('col-sm-4');
for (let i = 0; i < btn.length; i++) {
btn[i].onclick = function(event) {
if(event.target.tagName.toLowerCase() === 'div') {
modal.style.display = 'block';
let idDIVOrderParent = btn[i].id;
console.log('The order ID is ' + idDIVOrderParent);
let idDIVOrder = 'o' + idDIVOrderParent.substr(7);
modal.textContent = idDIVOrder;
console.log('The modal is currently displaying ' + idDIVOrder);
}
}
}
#myModal {
width: 200px;
text-align: center;
background: #ccc;
display: none;
}
<div id="parentOrderId9999" class="col-sm-4">btn1</div>
<div id="parentOrderId1111" class="col-sm-4">btn2</div>
<div id="parentOrderId2222" class="col-sm-4">btn3</div>
<div id="parentOrderId3333" class="col-sm-4">btn4</div>
<div id="myModal"></div>
Related
<button id="showlinks" onclick="myFunction">show</button>
<div id="buttonlinks"></div>
function myFunction() {
var button = document.createElement("button");
document.getElementById("buttonlinks").appendChild(button);
}
I used This Code to create buttons on clicking a button. when clicking the show button the buttons appear but after refresh they are gone.
can I store the buttons with localStorage?
You can store the information about your buttons in the localStorage whenever you create a button, and add an eventListener to window.onload to read the buttons from localStorage and append it to the page when page has loaded, in the below exmpale to keep it simple, I just store the length of buttons.
<button id="showlinks" onclick="myFunction">show</button> <div id="buttonlinks"></div>
js:
let buttonsLength = 0;
document.getElementById('showlinks').addEventListener('click', function () {
createButton();
buttonsLength++;
localStorage.setItem('buttonsLength', buttonsLength)
});
function createButton() {
var button = document.createElement('button');
button.innerHTML = 'click me';
document.getElementById('buttonlinks').appendChild(button);
}
window.addEventListener('load', (event) => {
buttonsLength = Number(localStorage.getItem('buttonsLength')) || 0;
for (let i = 0; i < buttonsLength; i++) {
createButton();
}
});
const showlinks = document.getElementById('showlinks')
showlinks.addEventListener("click", function () {
localStorage.setItem("showClicked", true)
displayButtonInDom()
})
function displayButtonInDom() {
const showClicked = localStorage.getItem("showClicked")
if (showClicked) {
const button = document.createElement("button");
button.innerText = "click me"
document.getElementById("buttonlinks").appendChild(button);
}
}
displayButtonInDom()
<button id="showlinks">show</button>
<div id="buttonlinks"></div>
Foreache button created you have to add the button information to the localstorage. And on page load you execute an init function that rebuild all button created from the localstorage
<button id="showlinks" onclick="createButton('')">show</button>
<div id="buttonlinks"></div>
<script>
function Initbutton() {
//on load check if the button has been already add using the localStorage
var buttons = getButtonInformationFromLocalStorage();
if(buttons != null){
buttons.forEach(function(btnName){
createButton(btnName);
})
}
}
// fo the purpose of having different button name
// i have picket this function here https://www.codegrepper.com/code-examples/javascript/find+random+name+javascript
function getRandomString(length) {
var randomChars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
var result = '';
for ( var i = 0; i < length; i++ ) {
result += randomChars.charAt(Math.floor(Math.random() * randomChars.length));
}
return result;
}
function createButton(btnName){
if(btnName == undefined || btnName == ""){
btnName = "button" + getRandomString(10);
updateButtonInformationToLocalStorage(btnName);
}
var button = document.createElement("button");
button.innerHTML = btnName;
document.getElementById("buttonlinks").appendChild(button);
}
function updateButtonInformationToLocalStorage(name){
var lstrg = localStorage.getItem("alreadyaddbuttontobuttonlinks");
if(lstrg == null){
localStorage.setItem("alreadyaddbuttontobuttonlinks", name);
return name;
}else{
var nLstrg = lstrg + "|" + name;
localStorage.setItem("alreadyaddbuttontobuttonlinks", nLstrg);
return nLstrg;
}
}
function getButtonInformationFromLocalStorage(){
var lstrg = localStorage.getItem("alreadyaddbuttontobuttonlinks");
if(lstrg == null){
return null;
}else{
return lstrg.split("|");
}
}
window.onload = Initbutton();
</script>
I want to count the number of buttons inside a div, I add the buttons dynamically from a js file. When I run the program on the browser, the buttons appear correctly and when I inspect the code from the browser all the elements and class names are correct. However, when I try to log the number of buttons with a class name of "accordion" it returns 0 when it should return 4.
Here's the HTML code:
<body class="d-flex justify-content-center" style="width: 100%;height: 50%;">
<div class="container" style="width: 500px;"><img src="assets/img/header_image.png" style="width: 100%;">
<div class="row" style="margin-top: 20px;">
<div id="accordion-div" class="col">
</div>
</div>
</div>
<script src="assets/js/jquery.min.js"></script>
<script src="assets/bootstrap/js/bootstrap.min.js"></script>
<script src="index.js"></script>
The javascript code:
var acc = document.getElementsByClassName("accordion");
var acc_div = document.getElementById("accordion-div")
var i;
add_accordion_item = (name, details) => {
var btn = document.createElement('button');
acc_div.appendChild(btn);
btn.innerHTML = name;
btn.className += "btn btn-success accordion";
btn.type = "button"
var details = document.createElement('div');
acc_div.appendChild(details);
details.innerHTML = details
details.className += "panel"
}
url = '.....'
make_list = () => {
$.getJSON(url, function(data){
for(var i = 0; i<data["number_of_threats"]; i++){
name = data["info"][i]["name"];
details = "details"
add_accordion_item(name, details);
}
});
}
make_list()
console.log(document.getElementsByClassName("accordion").length)
for (i = 0; i < acc.length; i++) {
acc[i].addEventListener("click", function() {
this.classList.toggle("active");
var panel = this.nextElementSibling;
if (panel.style.display === "block") {
panel.style.display = "none";
} else {
panel.style.display = "block";
}
});
}
This should show a button that when pressed it should show the div with the details. But the for never runs as the acc.length is 0.
Thanks for you time
I think that a node array don't get length attribut.
You should use forEach instead of for loop like
acc.forEach(function(element) {
element.addEventListener("click", function() {
this.classList.toggle("active");
var panel = this.nextElementSibling;
if (panel.style.display === "block") {
panel.style.display = "none";
} else {
panel.style.display = "block";
}
});
})
I hope it helps you
I think you have missed to add a class accordion as to any element. because I cant see any element with a class name as accordion in your html code.
But inside to your javascriptcode, on first line you are trying to search an element with a class name as accordion.
The variable acc doesn't auto update after you declared it in the beginning.
You will need to retrieve the value of acc variable again just above the for loop.
i.e. just above the for loop you need to add the line like this
acc = document.getElementsByClassName("accordion");
for (i = 0; i < acc.length; i++) {
....
I fixed it by adding the listener inside the make_list function
var acc = document.getElementsByClassName("accordion");
var acc_div = document.getElementById("accordion-div")
var i;
add_accordion_item = (name, details) => {
var btn = document.createElement('button');
acc_div.appendChild(btn);
btn.innerHTML = name;
btn.className += "btn btn-success accordion";
btn.type = "button"
var details = document.createElement('div');
acc_div.appendChild(details);
details.innerHTML = details;
details.className += "panel";
add_listener(btn);
}
url = '.....'
make_list = () => {
$.getJSON(url, function(data){
for(var i = 0; i<data["number_of_threats"]; i++){
name = data["info"][i]["name"];
details = "details"
add_accordion_item(name, details);
}
});
}
make_list()
function add_listener(element) {
element.addEventListener("click", function() {
this.classList.toggle("active");
var panel = this.nextElementSibling;
if (panel.style.display === "block") {
panel.style.display = "none";
} else {
panel.style.display = "block";
}
});
}
The first part of the code is working correctly, but now that each button appears, how do i add functionality to each of them? currently the only button which does something when pressed is always the last one, the rest do nothing.
Change it to
{
var output = "";
var data = JSON.parse(e.target.responseText);
for(var i=0; i<data.length; i++)
{
output = data[i].title + ' ';
var p = document.createElement("p");
var div = document.getElementById("response");
var textNode = document.createTextNode(output);
p.appendChild(textNode);
var button = document.createElement("button");
button.innerHTML = "Download";
p.appendChild(button);
div.appendChild(p);
button.addEventListener ("click", () =>
{
alert("Test");
});
}
}
You are adding the below code out side the for loop
button.addEventListener ("click", () =>
{
alert("Test");
} );
Keep the above code inside the for loop. So that for each button the event listener will be added.
Another way to approach this would be to add the callback function to the onclick variable of the elements prototype:
function doStuff() {
var output = "";
var data = JSON.parse(e.target.responseText);
for (var i = 0; i < data.length; i++) {
output = data[i].title + ' ';
var p = document.createElement("p");
var div = document.getElementById("response");
var textNode = document.createTextNode(output);
p.appendChild(textNode);
var button = document.createElement("button");
button.innerHTML = "Download";
// Adds the callback function here
button.onclick = () => {
// fill in your arrow function here...
alert("Test");
};
p.appendChild(button);
div.appendChild(p);
};
}
doStuff();
Here is a jsFiddle
You should use event delegation for dynamically added elements
// sample data
var data = [{
title: 'one'
}, {
title: 'two'
},{
title: 'three'
}];
var output = "";
for (var i = 0; i < data.length; i++) {
var output = data[i].title + " ";
var p = document.createElement("p");
var div = document.getElementById("response");
var textNode = document.createTextNode(output);
p.appendChild(textNode);
var button = document.createElement("button");
// added output to button text for identification
button.innerHTML = output + " Download";
p.appendChild(button);
div.appendChild(p);
}
// Get the parent element, add a click listener
document.getElementById("response").addEventListener("click", function(e) {
// e.target is the clicked element!
// If it was a button
if (e.target && e.target.nodeName == "BUTTON") {
// Button found! Output the identifying data!
// do other work on click
document.getElementById("display").innerHTML = e.target.innerHTML + " Clicked";
}
});
<div id="response"></div>
<div id="display">Display</div>
I wrote a bunch of JS code to add click listeners to the HTML buttons. I have some CSS that applies to the existing HTML. But the CSS doesn't appear to apply to the HTML generated by the JavaScript.
Link to JSfiddle
As you can see, line 1 has all the proper spacing. But any of the generated lines by clicking any of the three buttons lose the spacing.
Here's the CSS:
.form-block {
padding: 5px;
margin: 5px;
border: 1px solid #000;
span {
padding-right: 15px;
}
}
Thanks for the help in advance! :)
What you see are actual spaces between the elements in your original HTML/row, and then a lack of spaces between the elements in your JS generated elements.
I would either remove the spaces between the elements in your HTML, and use a left/right margin value to create that space instead, or add a space or carriage return/new line between the elements when you add them in your JS.
I removed the spaces (by inserting HTML comments) in your original HTML, and added margin-right: 1em to the input and buttons in your element row.
/*This function interprets the line position.
//input is a lpos string which is in the form of x.x.x.x
//splits it by the '.'
//returns the array with position
*/
function lposToAr(lpos) {
var ar = lpos.split('.');
for (var i = 0; i < ar.length; i++) {
ar[i] = parseInt(ar[i], 10);
}
return ar;
}
//This function turns the position array back into a string
function lposToStr(posAr) {
return posAr.join('.');
}
//This function creates a new line after clicking the NL button.
function NL(lpos, e) {
e.preventDefault();
var cLine = document.getElementsByClassName('line')[0].cloneNode(true);
var cNL = document.getElementsByClassName('new-line')[0].cloneNode(true);
var cNN = document.getElementsByClassName('new-nested')[0].cloneNode(true);
var cI = document.getElementsByClassName('input')[0].cloneNode(true);
cI.value = '';
var cNI = document.getElementsByClassName('new-input')[0].cloneNode(true);
//figure out which position
var posAr = lposToAr(lpos);
var clickNode = document.getElementsByTagName('form')[0];
for (var i = 0; i < posAr.length; i++) {
clickNode = clickNode.children;
var skipCount = 0;
for (var j = 0; j < clickNode.length - 1; j++) {
if (clickNode[j].tagName != 'DIV') {
skipCount++;
}
}
clickNode = clickNode[posAr[i] + skipCount - 1];
}
//set the last element of the posAr the total number of same level <div>+1
var siblingDivCount = 0;
for (var i = 0; i < clickNode.parentNode.children.length; i++) {
if (clickNode.parentNode.children[i].tagName == 'DIV') {
siblingDivCount++;
}
}
var newPosAr = posAr;
newPosAr[newPosAr.length - 1] = siblingDivCount + 1;
//Update the cloned items with the new line info
var newPosStr = lposToStr(newPosAr);
cLine.innerHTML = newPosStr;
cNL.addEventListener('click', function(event) {
NL(newPosStr, event);
});
cNN.addEventListener('click', function(event) {
NN(newPosStr, event);
});
//cI doesn't need to change, unless there's a bug
cNI.addEventListener('click', function(event) {
NI(newPosStr, event);
});
//Make the new Div
var newDiv = document.createElement('div');
newDiv.setAttribute('class', 'form-block');
newDiv.appendChild(cLine);
newDiv.appendChild(cNL);
newDiv.appendChild(cNN);
newDiv.appendChild(cI);
newDiv.appendChild(cNI);
//Add the new Div
clickNode.parentNode.appendChild(newDiv);
}
function NN(lpos, e) {
e.preventDefault();
var cLine = document.getElementsByClassName('line')[0].cloneNode(true);
var cNL = document.getElementsByClassName('new-line')[0].cloneNode(true);
var cNN = document.getElementsByClassName('new-nested')[0].cloneNode(true);
var cI = document.getElementsByClassName('input')[0].cloneNode(true);
cI.value = '';
var cNI = document.getElementsByClassName('new-input')[0].cloneNode(true);
var posAr = lposToAr(lpos);
var clickNode = document.getElementsByTagName('form')[0];
for (var i = 0; i < posAr.length; i++) {
clickNode = clickNode.children;
var skipCount = 0;
for (var j = 0; j < clickNode.length - 1; j++) {
if (clickNode[j].tagName != 'DIV') {
skipCount++;
}
}
clickNode = clickNode[posAr[i] + skipCount - 1];
}
var childDivCount = 0;
for (var k = 0; k < clickNode.children.length; k++) {
if (clickNode.children[k].tagName == 'DIV') {
childDivCount++;
}
}
posAr.push(childDivCount + 1);
//Update the cloned items with the new line info
var newPosStr = lposToStr(posAr);
cLine.innerHTML = newPosStr;
cNL.addEventListener('click', function(event) {
NL(newPosStr, event);
});
cNN.addEventListener('click', function(event) {
NN(newPosStr, event);
});
//cI doesn't need to change, unless there's a bug
cNI.addEventListener('click', function(event) {
NI(newPosStr, event);
});
//Make the new Div
var newDiv = document.createElement('div');
newDiv.className = 'form-block';
newDiv.appendChild(cLine);
newDiv.appendChild(cNL);
newDiv.appendChild(cNN);
newDiv.appendChild(cI);
newDiv.appendChild(cNI);
//Add the new Div
clickNode.appendChild(newDiv);
}
function NI(lpos, event) {
event.preventDefault();
//copy text box
var cI = document.getElementsByClassName('input')[0].cloneNode(true);
cI.value = '';
//find the node that has been clicked
var posAr = lposToAr(lpos);
var clickNode = document.getElementsByTagName('form')[0];
for (var i = 0; i < posAr.length; i++) {
clickNode = clickNode.children;
var skipCount = 0;
for (var j = 0; j < clickNode.length - 1; j++) {
if (clickNode[j].tagName != 'DIV') {
skipCount++;
}
}
clickNode = clickNode[posAr[i] + skipCount - 1];
}
//add the new textbox
clickNode.insertBefore(cI, clickNode.getElementsByClassName('new-input')[0]);
}
document.getElementsByClassName('new-line')[0].addEventListener('click', function(event) {
NL(document.getElementsByClassName('line')[0].innerHTML, event);
});
document.getElementsByClassName('new-nested')[0].addEventListener('click', function(event) {
NN(document.getElementsByClassName('line')[0].innerHTML, event);
});
document.getElementsByClassName('new-input')[0].addEventListener('click', function(event) {
NI(document.getElementsByClassName('line')[0].innerHTML, event);
});
/**
* Add styling to the form to make it look appealing
*/
.form-block {
padding: 5px;
margin: 5px;
border: 1px solid #000;
}
span {
padding-right: 15px;
}
.form-block button, .form-block input {
margin-right: 1em;
}
<!--
DO NOT CHANGE THE CONTENTS OF THIS BLOCK
-->
<form class='myform'>
<div class='form-block'>
<span class='line'>1</span><!--
--><button class='new-line'>New Line</button><!--
--><button class='new-nested'>New Nested Line</button><!--
--><input class='input' type='text' placeholder='Enter Value...'><!--
--><button class='new-input'>Add input</button>
</div>
</form>
Here is my html with the JS within. The function "toggleMessage()" is the function I am using to hide and show messages when clicked. The problems I am running into include:
Messages showing by default, when they should hide by default (until clicked).
When clicked, all messages show at once - I want only the messages clicked on to show.
The new messages that populate the page every 3 seconds are not affected by the click event - I want them to be effected.
function newMessage() {
// Loop goes through emails and creats divs for subject, date, sender, and body
var i = 0;
while (i < window.geemails.length) {
//email container
var div = document.createElement("div");
//date
var dateField = document.createElement("h1");
dateField.className = "date";
dateField.innerHTML = window.geemails[i].date;
div.appendChild(dateField);
//subject
var subjectField = document.createElement("h1");
subjectField.className = "subject";
subjectField.innerHTML = window.geemails[i].subject;
div.appendChild(subjectField);
//sender
var senderField = document.createElement("h1");
senderField.className = "sender";
senderField.innerHTML = window.geemails[i].sender;
div.appendChild(senderField);
//body
var bodyField = document.createElement("p");
bodyField.className = "body";
bodyField.innerHTML = window.geemails[i].body;
div.appendChild(bodyField);
//Separate message content into containers
document.getElementById("main").appendChild(div);
//inbox counter
document.getElementById('counter').innerHTML = "You have " + document.getElementsByClassName("date").length + " messages";
window.geemails.shift();
}
}
newMessage();
//interval for loading new messages
function addNewMessage() {
setInterval(function() {
window.geemails.push(getNewMessage());
newMessage();
}, 3000);
}
addNewMessage();
//hiding and showing body message upon click
var messageBox = document.getElementsByClassName("subject");
for (var i = 0; i < messageBox.length; i++) {
messageBox[i].addEventListener("click", toggleMessage);
}
function toggleMessage() {
var showMessage = document.getElementsByClassName('body');
for (var i = 0; i < showMessage.length; i++) {
if (showMessage[i].style.display === 'none') {
showMessage[i].style.display = "block";
} else {
showMessage[i].style.display = "none";
}
}
}
You need a class for your messages and possiblt for their bodies.
Suppose you have a message div with a message body like this
<div class="message">
<div class="message-header">
</div>
<div class="message-body" style="display:none;">
</div>
</div>
if you want the body to be hidden by default use css display:none
then in your javascript do
$('.message').click(function(){
$(this).children('.message-body').show();
});