Switch paragraphs using inner HTML in JS - javascript

I am trying to switch two paragraphs after clicking the button but I am stuck. Is there any way how to do this using inner HTML without using IF or boolean? I tried this code but it doesn't work. Thanks
let elmsButton = document.querySelectorAll("button")[0];
let elmsParagraf1 = document.querySelectorAll(".prvni")[0];
let elmsParagraf2 = document.querySelectorAll(".druhy")[0];
elmsButton.addEventListener("click", () => {
elmsParagraf1.innerHTML = "<div class='druhy'></div>"
elmsParagraf2.innerHTML = "<div class='prvni'></div>"
});

Assign each DOM.innerHTML of paragraph to a variable then swap them like below example:
let elmsButton = document.querySelectorAll("button")[0];
let elmsParagraf1 = document.querySelectorAll(".prvni")[0];
let elmsParagraf2 = document.querySelectorAll(".druhy")[0];
elmsButton.addEventListener("click", () => {
const p1 = elmsParagraf1.innerHTML;
const p2 = elmsParagraf2.innerHTML
elmsParagraf1.innerHTML = p2;
elmsParagraf2.innerHTML = p1
});
<button>Click</button>
<div class='prvni'>Paragraph 1</div>
<div class='druhy'>Paragraph 2</div>

Why don't you use querySelector in place of using querySelectorAll and choose the first element?
By the way, I advise to delete and re-add the elements from the parent rather than using innerHTML. The use of innerHTML would not preserve listeners and have worse performances:
let elmsButton = document.querySelector("button");
let elmsParagraf1 = document.querySelector(".prvni");
let elmsParagraf2 = document.querySelector(".druhy");
elmsButton.addEventListener("click", () => {
swapElements(elmsParagraf1, elmsParagraf2);
});
function swapElements(elem1, elem2) {
// Check if siblings
if (elem1.parentElement !== elem2.parentElement) {
console.error('No sibling elements!');
return;
}
elem1.replaceWith(elem2.cloneNode(true));
elem2.replaceWith(elem1.cloneNode(true));
}
Example:
let elmsButton = document.querySelector("button");
elmsButton.addEventListener("click", () => {
let elmsParagraf1 = document.querySelector(".prvni");
let elmsParagraf2 = document.querySelector(".druhy");
swapElements(elmsParagraf1, elmsParagraf2);
});
function swapElements(elem1, elem2) {
// Check if siblings
if (elem1.parentElement !== elem2.parentElement) {
console.error('No sibling elements!');
return;
}
elem1.replaceWith(elem2.cloneNode(true));
elem2.replaceWith(elem1.cloneNode(true));
}
<button>Click me</button>
<div class="prvni">I am the first div</div>
<div class="druhy">I am the second div</div>

You can switch those two divs by creating a temporary variable that holds Paragraf1 and then change Paragraf1 to Paragraf2 and vice versa, but with the variable.
let temp = elmsParagraf1.innerHTML;
elmsParagraf1.innerHTML = elmsParagraf2.innerHTML
elmsParagraf2.innerHTML = temp

Related

Javascript - Navigation as a loop

I would like to create a simple navigation:
<ul>
<li>Div1-name</li>
<li>Div2-name</li>
<li>Div3-name</li>
</ul>
When clicked, it goes to the div with that id. I don't want to do this permanently, it's supposed to change in the loop because the user can add new items and wants them to be looped.
The user adds new div - with a unique name and ID. How to construct a loop?
It's best to set a constant class for divs (e.g. gohere), you have to load it via javascript, then do li elements in a loop.
Can anyone help?
And these are the elements that the user adds:
<div class="divdiv" id="div_id">
<h3>DIV TITLE</h3>
<br>
Description
<br>
<p>Description</p>
<hr>
</div>
OK, it's really difficult to understand exactly what you ask for, but here (a bit hacky) an example. I hope that it can help you in the right direction.
var items = document.getElementById('items');
var main = document.getElementById('main');
items.addEventListener('click', e => {
e.preventDefault();
if (e.target.nodeName == 'A') {
console.log(`You clicked ${e.target.attributes['href'].value}`);
}
});
document.forms.new.addEventListener('submit', e => {
e.preventDefault();
let newid = items.children.length + 1;
console.log(newid);
let li = `<li>Div${newid}-name</li>`;
items.innerHTML += li;
let div = `<div class="divdiv" id="div${newid}">
<h3>DIV TITLE</h3><br>Description<br><p>Description</p><hr></div>`;
main.innerHTML += div;
});
<ul id="items">
<li>Div1-name</li>
<li>Div2-name</li>
<li>Div3-name</li>
</ul>
<form name="new">
<button>Add new</button>
</form>
<div id="main"></div>
Keep the navigation details in an array, and then iterate over it.
Use DOMParser to parse the HTML the user adds to extract the navigation information.
const arr = [
{ href: "div1", name: 'Div1-name' },
{ href: "div2", name: 'Div2-name' },
{ href: "div3", name: 'Div3-name' }
];
const nav = document.querySelector('#nav');
const main = document.querySelector('#main');
const text = document.querySelector('textarea');
const button = document.querySelector('button');
button.addEventListener('click', handleInput, false);
const parser = new DOMParser();
function handleInput() {
// Add the HTML to the page
main.insertAdjacentHTML('beforeend', text.value);
// Parse the HTML
const frag = parser.parseFromString(text.value, 'text/html');
// Extract the id (href) and name
const href = frag.querySelector('div').id;
const name = frag.querySelector('h3').textContent;
// Add this to the navigation array
// to the array
arr.push({ href: `#${href}`, name: name });
updateView();
}
function updateView() {
// `map` is useful here. It will produce a new array
// (of HTML), but make sure you `join` it up at the end
const links = arr.map(({ href, name }) => {
return `<li>${name}</li>`;
}).join('');
nav.innerHTML = `<ul>${links}</ul>`;
}
updateView();
<div id="nav"></div>
Add HTML:
<textarea></textarea>
<button>Add</button>
<div id="main"></div>

How Dynamically Created Button Target Individual Element

I am under the impression if we target an element by element name of class name it will be applicable to all elements or elements with that class name. But in the following code when I clicked a button it will only change the innerHTML of the nearest span element even though I have add event listener by selecting just button element. Please clarify. Thanks.
const myArray = [0, 1, 2];
let myContainer = document.querySelector('.container');
const newArray = myArray.map((item) => {
let newArticle = document.createElement('article');
const myHTML = `<article>
<span></span>
<button>Click ${item}</button>
</article>
`;
newArticle.innerHTML = myHTML;
let myBtn = newArticle.querySelector('button');
myBtn.addEventListener('click', (e) => {
newArticle.querySelector('span').innerHTML = 'clicked';
})
return newArticle;
});
newArray.forEach((item) => {
myContainer.appendChild(item);
});
<div class="container">
<section>
</section>
</div>
let myBtn = newArticle.querySelector('button');
returns the FIRST button in newArticle
let myBtn = document.querySelectorAll('button');
returns ALL buttons in document
You can tell us what you expect by clicking on each one button?

How to assign an event listener to every link I create in a forEach loop in Javascript

I am using a forEach loop and creating a link based on a condition. However, the problem I am facing is that the event listener is only added to the last item. I know this question has been asked before and I know the reason for the issue is Hoisting and I should use closure. I have tried to do that but I do not think I am doing it correctly. I would really appreciate any help as I am kind of stuck. Below are some snippets from my code (I have deleted some pieces from the code for the purpose of the question):
function edit(post, post_id){
alert(post_id);
}
function load_posts(event) {
fetch(`/posts?start=${start}&end=${end}`)
.then(response => response.json())
.then(data => {
document.querySelector('#posts').innerHTML = "<div></div>";
let c = 0;
data.forEach(i => {
if (c === data.length - 1){
return;
};
document.querySelector('#posts').innerHTML += `<div class = 'border' >`
let current_user = document.getElementById("logged_in").innerHTML;
document.querySelector('#posts').innerHTML += `<div class="row" ><div class="col-2"
id = border${i.id}></div>
</div></div>`;
if (i.user_id === parseInt(current_user)){
let element = document.createElement('a');
element.innerHTML = "Edit";
element.setAttribute('data-id', `${i.id}`);
element.setAttribute('data-post', `${i.id}`);
element.setAttribute('href', `#${i.id}`);
element.addEventListener('click', function()
{edit(this.dataset.id,this.dataset.post);});
console.log(element);
document.querySelector(`#border${i.id}`).append(element);
};
}
c++;
});});
Assigning to the innerHTML of an element will corrupt any existing listeners its children have:
document.querySelector('button').addEventListener('click', () => {
console.log('clicked');
});
container.innerHTML += '';
// click listener doesn't work anymore now
<div id="container">
<button>click</button>
</div>
Create the element with document.createElement instead of concatenating an HTML string:
const currentUserId = Number(document.getElementById("logged_in").textContent);
data.slice(0, data.length - 1).forEach((i, index) => {
const newPost = document.querySelector('#posts').appendChild(document.createElement('div'));
newPost.className = 'border';
newPost.innerHTML = `<div class="row" ><div class="col-2"
id = border${i.id}></div>
</div>`;
if (i.user_id === currentUserId) {
let element = document.createElement('a');
element.textContent = "Edit";
element.dataset.id = i.id;
element.dataset.post = i.id;
element.href = `#${i.id}`;
element.addEventListener('click', function() {
edit(this.dataset.id, this.dataset.post);
});
document.querySelector(`#border${i.id}`).append(element);
}
});
I'd also highly recommend changing your i variable name to something else - i is almost always used to indicate the index that you're iterating over. The element being iterated over should not be named i - call it something like postData instead, to avoid confusion.

Struggling to keep HTML and JS separate

I'm creating a basic to do list in Vanilla JS, I'm using Handlebars to keep the HTML & JS separate.
Everything was going fine till I came to the delete method. Because my delete button is inside my HTML and not created inside my JS I'm finding it hard to select and delete items from the array.
I thought I'd found a way around it by looping over them but the issue with this is it tries to grab the buttons on page load, and so it returns always an empty array as on page load there are no delete buttons as no to do has been added at that point.
I've also tried putting the delete method inside the add method to counter this but this also presented issues.
Simply, can someone give me an example of a working delete method that removes the relevant item from the array using splice.
Cheers
HTML
<input id="add-to-do-value" type="text" placeholder="Add to do">
<button id="add-to-do">Add</button>
<div id="to-do-app"></div>
<script type="text/javascript" src="js/handlebars.js"></script>
<script id="to-do-template" type="text/template">
<ul>
{{#this}}
<div>
<li id={{id}}>
{{value}}
<button class="delete-btn" id={{id}}>Delete</button>
</li>
</div>
{{/this}}
</ul>
</script>
<script type="text/javascript" src="js/app.js"></script>
JS
(function() {
// Data array to store to dos
var data = [];
// Cache dom
var toDoApp = document.getElementById('to-do-app');
var toDoTemplate = document.getElementById('to-do-template');
var addToDo = document.getElementById('add-to-do');
var addToDoValue = document.getElementById('add-to-do-value');
var toDoTemplate = Handlebars.compile(toDoTemplate.innerHTML);
// Render HTML
var render = function() {
toDoApp.innerHTML = toDoTemplate(data);
}
// Add to dos
var add = function() {
var toDoValue = addToDoValue.value;
if(toDoValue) {
var toDoObj = {
value: toDoValue,
id: Date.now(),
}
data.push(toDoObj);
}
render();
}
// Delete to dos
var deleteBtn = document.querySelectorAll('.delete-btn');
for(i=0; i<deleteBtn.length; i++) {
deleteBtn[i].addEventListener("click", function(){
for(j=0; j<data.length; j++) {
if(data[j].id == this.id) {
data.splice(data[j], 1);
render();
}
}
});
}
// Bind events
addToDo.addEventListener("click", add);
})();
The fact that you're using Handlebars makes the whole thing unnecessary complex. I would suggest that you don't use innerHTML, but other parts of the DOM API instead to be able to easily access the elements you need. For more complex todo items, I would consider using <template>s.
Anyway, you have to bind the event listener for removing the item when you create the new item (i.e. in the add function):
var todos = [];
var input = document.querySelector('input');
var addButton = document.querySelector('button');
var container = document.querySelector('ul');
var add = function () {
var content = input.value;
input.value = '';
var id = Date.now();
var li = document.createElement('li');
li.appendChild(document.createTextNode(content));
var button = document.createElement('button');
button.textContent = 'Delete';
button.addEventListener('click', remove.bind(null, id));
li.appendChild(button);
todos.push({ content, id, element: li });
container.appendChild(li);
};
var remove = function (id) {
var todo = todos.find(todo => todo.id === id);
container.removeChild(todo.element);
todos = todos.filter(t => t !== todo);
};
addButton.addEventListener('click', add);
<input type="text" placeholder="Add to do">
<button>Add</button>
<ul></ul>

CreateElement with id?

I'm trying to modify this code to also give this div item an ID, however I have not found anything on google, and idName does not work. I read something about append, however it seems pretty complicated for a task that seems pretty simple, so is there an alternative? Thanks :)
g=document.createElement('div'); g.className='tclose'; g.v=0;
You should use the .setAttribute() method:
g = document.createElement('div');
g.setAttribute("id", "Div1");
You can use g.id = 'desiredId' from your example to set the id of the element you've created.
var g = document.createElement('div');
g.id = 'someId';
You can use Element.setAttribute
Examples:
g.setAttribute("id","yourId")
g.setAttribute("class","tclose")
Here's my function for doing this better:
function createElement(element, attribute, inner) {
if (typeof(element) === "undefined") {
return false;
}
if (typeof(inner) === "undefined") {
inner = "";
}
var el = document.createElement(element);
if (typeof(attribute) === 'object') {
for (var key in attribute) {
el.setAttribute(key, attribute[key]);
}
}
if (!Array.isArray(inner)) {
inner = [inner];
}
for (var k = 0; k < inner.length; k++) {
if (inner[k].tagName) {
el.appendChild(inner[k]);
} else {
el.appendChild(document.createTextNode(inner[k]));
}
}
return el;
}
Example 1:
createElement("div");
will return this:
<div></div>
Example 2:
createElement("a",{"href":"http://google.com","style":"color:#FFF;background:#333;"},"google");`
will return this:
google
Example 3:
var google = createElement("a",{"href":"http://google.com"},"google"),
youtube = createElement("a",{"href":"http://youtube.com"},"youtube"),
facebook = createElement("a",{"href":"http://facebook.com"},"facebook"),
links_conteiner = createElement("div",{"id":"links"},[google,youtube,facebook]);
will return this:
<div id="links">
google
youtube
facebook
</div>
You can create new elements and set attribute(s) and append child(s)
createElement("tag",{attr:val,attr:val},[element1,"some text",element2,element3,"or some text again :)"]);
There is no limit for attr or child element(s)
Why not do this with jQuery?
var newDiv= $('<div/>', { id: 'foo', class: 'tclose'})
var element = document.createElement('tagname');
element.className= "classname";
element.id= "id";
try this you want.
that is simple, just to make a new element with an id :
var myCreatedElement = document.createElement("div");
var myContainer = document.getElementById("container");
//setAttribute() is used to create attributes or change it:
myCreatedElement.setAttribute("id","myId");
//here you add the element you created using appendChild()
myContainer.appendChild(myCreatedElement);
that is all
I'm not sure if you are trying to set an ID so you can style it in CSS but if that's the case what you can also try:
var g = document.createElement('div');
g.className= "g";
and that will name your div so you can target it.
window.addEventListener('DOMContentLoaded', (event) => {
var g = document.createElement('div');
g.setAttribute("id", "google_translate_elementMobile");
document.querySelector('Selector will here').appendChild(g);
});

Categories