How do I toggle DOM elements to show and hide - javascript

On my index.JS file, I created elements using documentCreateElement and appended them to the DOM to show data from an api. Then I created an event listener which works where if I click my H3 element, it will show my H4 and P element. But I want it to where if I click my H3 element again, I want the H4 and P elements to hide. This is the code I have for my click event listener:
`H3.addEventListener(‘click’, clickFunction)
Function clickFunction() {
Div.append(h4)
Div.append(p)
}`
Can someone please help me?
I tried to look up toggling functions online or incorporate CSS hidden class to the h4 and p elements but nothing was hiding

Try modifying your clickFunction() to check if the elements are already displayed or not.
let isDisplayed = false;
function clickFunction() {
if (isDisplayed) {
// If the elements are already displayed, hide them
h4.style.display = "none";
p.style.display = "none";
isDisplayed = false;
} else {
// If the elements are not displayed, show them
div.appendChild(h4);
div.appendChild(p);
isDisplayed = true;
}
}
h3.addEventListener("click", clickFunction);
In above example, its a boolean variable isDisplayed to keep track of whether the elements are currently displayed or not. When the h3 element is clicked, the clickFunction() is called, and it checks the value of isDisplayed. If its true, it means the elements are already displayed, so we hide them by setting their display style to "none" and setting isDisplayed to false. If it's false, it means the elements are not displayed, so we show them by appending them to the div and setting isDisplayed to true.
Note that you'll need to deeclare the isDisplayed variable outside of the clickFunction() so that its value is preserved between function calls. And make sure to select the h4, p, and div elements using document.querySelector() or a smilar method before using them in the function.

You can try this in your clickFunction
Function clickFunction() {
if(!Div.hasChildNodes()) {
Div.append(h4)
Div.append(p)
} else {
Div.removeChild(Div.childNodes(0))
Div.removeChild(Div.childNodes(1))
}
}
Check this for more info.

Follow the below steps-
Find the existing p and h4 elements.
If found then remove those using the removeChild method, else append those using the append method.
Here is a working demo of toggling the elements-
let h3 = document.querySelector('h3');
h3.addEventListener('click', clickFunction)
let div = document.querySelector('div');
function clickFunction() {
// Find already exists p and h4 elements
let p_exists = document.querySelector('p');
let h4_exists = document.querySelector('h4');
// If found then remove one by one
if (p_exists && h4_exists) {
div.removeChild(h4_exists);
div.removeChild(p_exists);
}
// Else, create and append
else {
let h4 = document.createElement('h4')
h4.innerText = "I am a h4 element";
let p = document.createElement('p')
p.innerText = "I am a p element";
div.append(h4, p);
}
}
<h3>Click</h3>
<div></div>

Using a CSS style rule and checking for the existence of the CSS style rule class name on each HTML element's class list is a common method for toggling the display state of an element.
I used a hide class name in the code snippet but you can change to a 'show' class name with some minor modifications to the code and CSS rules.
var H3 = document.getElementById("h3-elem");
H3.addEventListener("click", clickFunction);
var Div = document.getElementById("div-elem");
function clickFunction() {
var h4 = document.getElementById("h4-elem");
var p = document.getElementById("p-elem");
if (!h4) {
h4 = appendH4();
}
if (!p) {
p = appendP();
}
// Option 1:
// Use the built-in 'toggle()' method on the class list
// for each element.
// https://developer.mozilla.org/en-US/docs/Web/API/DOMTokenList/toggle
// h4.classList.toggle("hide");
// p.classList.toggle("hide");
// Option 2:
// Check if the class list for the 'h4' and 'p' elements
// contain 'hide' class name. If the class list does not
// contain the class name then add the 'hide' class name
// to the class list.
if (h4.classList.contains("hide")) {
h4.classList.remove("hide");
} else {
h4.classList.add("hide");
}
if (p.classList.contains("hide")) {
p.classList.remove("hide");
} else {
p.classList.add("hide");
}
}
// Set to hide initially. After creation of the element,
// the 'clickFunction' will determine the display state
// of the element by checking for the existense of the
// 'hide' class name.
function appendH4() {
var h4 = document.createElement("h4");
h4.id = "h4-elem";
h4.className = "hide";
h4.textContent = "Header 4";
Div.append(h4);
return h4;
}
function appendP() {
var p = document.createElement("p");
p.id = "p-elem";
p.className = "hide";
p.textContent = "A paragraph";
Div.append(p);
return p;
}
.hide {
display: none;
}
<h3 id="h3-elem">Header 3</h3>
<div id="div-elem"></div>

Related

Toggle Two Divs Onclick JavaScript

I am writing a function that toggles two divs onclick. I have 2 functions that displays an api json data as a table and graph (formatGraph and formatTable).
i have created two different divs and appended the formatGraph and formatTable functions to those divs. Then created a mainDiv to wrap the two divs.
I have an if statement that checks for when to show or hide the divs. I am calling the formatData function somewhere in my fetch call while rendering my result. I have a css file with the following code:
.displayTable {
display: block;
}
.displayGraph{
display: none;
}
But I am currently getting undefined and
Uncaught TypeError: Cannot read properties of null (reading 'style')
at HTMLButtonElement.
Any idea what I am missing, or how I can go about this? Here's my function:
const formatData = (response) => {
const mainDiv = document.createElement("div");
const div1= document.createElement("div");
div1.classList.add("displayTable");
div1.appendChild(formatTable(response));
mainDiv.appendChild(div1)
const div2 = document.createElement("div");
div2.classList.add("displayGraph");
div2.appendChild(formatGraph(response));
mainDiv.appendChild(div2)
viewTable.addEventListener("click", function() {
if(document.querySelector('.displayTable').style.display === "none") {
document.querySelector('.displayGraph').style.display = 'none';
document.querySelector('.displayTable').style.display = 'block';
}
else{
document.querySelector('.displayTable').style.display === "none"
document.querySelector('.displayGraph').style.display = 'block';
}
})
};
your code looks OK, but you're forgetting to append mainDiv to the document. That's why you're getting null as a result of calling document.querySelector(".displayTable") and document.querySelector(".displayGraph"). Using the access operator in null results in a TypeError.
//...
const div2 = document.createElement("div");
div2.classList.add("displayGraph");
div2.appendChild(formatGraph(response));
mainDiv.appendChild(div2)
document.body.appendChild(mainDiv);
//...
I've just used .appendChild in body, which appends the element at the end of the body. There are different methods that let you append elements at different locations of the DOM three. You could also create an empty element (has no children) in your HTML and use that element as a wrapper to append the DIVS.
Also, consider an improvement in your handler:
viewTable.addEventListener("click", function() {
// Execute document.querySelector once.
const displayTableDiv = document.querySelector('.displayTable')
const displayGraphDiv = document.querySelector('.displayGraph')
/* This new condition will prevent the Type Error if any. */
if (displayTableDiv && displayGraphDiv) {
if(displayTableDiv.style.display === "none") {
displayGraphDiv.style.display = 'none';
displayTableDiv.style.display = 'block';
}
else {
displayTableDiv.style.display === "none"
displayGraphDiv.style.display = 'block';
}
}
})

H1 within DIV not responding to ActionListener which is set for DIV - pure JavaScript

So basicily what I am trying to do is to set ActionListener for whole div that I'm generating after some user action. Inside of that div I have an h1 marker and some text. When I click on the h1 the ActionListener is not responding. If I click anywhere else inside of div everything is working properly. I am setting ActionListener for parent div with id "list".
document.getElementById("list").addEventListener("click", function(event) {
if ( event.target.className === 'note') {
var id = event.target.id.slice(-1);
document.getElementById("title").value = titles[id];
document.getElementById("typedtext").value = notes[id];
}
});
this is how I generate the DIV:
const divNote = document.createElement("div");
const h1 = document.createElement("H1");
h1.setAttribute("id", "h" + number_of_notes);
const titleNode = document.createTextNode(title);
h1.appendChild(titleNode);
const textNode = document.createTextNode(text);
divNote.classList.add("note");
divNote.setAttribute("id", "n" + number_of_notes);
divNote.appendChild(h1);
divNote.appendChild(textNode);
document.getElementById("list").appendChild(divNote);
How to make the whole div to be sensitive to ActionListener?
You shouldn't update the html with innerHTML, but rather append a div to your parent with appendChild.
Here's what it would look like:
const myElement = document.createElement("div");
myElement.classList.add("note");
/* Other stuff to create the div... */
document.getElementById("list").appendChild(myElement)
The problem with modifying the innerHTML is that this will overwrite any previous DOM and all of your eventListeners will be detached.
cf: addEventListener gone after appending innerHTML

Simplify code by applying property to multiple variables at once

I want to simplify this code
let a = document.querySelector(".arrow");
b = document.querySelector(".demo-desc1");
c = document.querySelector(".demo-title h3");
a.style.display = "none";
b.style.display = "none";
c.style.display = "none";
so I don't have to write style.display = "none" for every variable but rather to apply the propery for all variables at once.
Thank you.
If there's only one element of each in the DOM, put them all into a selector string, then iterate over the matching elements.
for (const elm of document.querySelectorAll('.arrow, .demo-desc1, .demo-title h3')) {
elm.style.display = 'none';
}
If there are multiple such elements, then you'll need
const selectors = ['.arrow', '.demo-desc1', '.demo-title h3'];
for (const s of selectors) {
document.querySelector(s).style.display = 'none';
}
But, in this sort of situation, an even better approach would be to toggle a class of a parent container, and have CSS rules that hide those elements when the class is on the parent container. I don't know what the rest of your HTML is like, but perhaps something like
<div class="demo-container">
<more HTML here>
</div>
.hide-children .arrow, .hide-children .demo-desc1, .hide-children .demo-title h3 {
display: none;
}
Then all you need is
document.querySelector('.demo-container').classList.add('hide-children');

if element is displayed > hide all elements underneath it (plain JS)

My goal is to detect if my element is displayed firstly; I guess narrowed down to display: block;.
If it is; I'd like to iterate through all the elements underneath it within the document to add classes to them. The elements are dynamic; so I just need to add a common selector so that I can hide them (or display: none;) them - when my first element's display: block; is met.
Below is what I have; I'm mainly wondering how to detect if elements are underneath #banner > if so add a common selector (class) to them so that I can then hide (display:none;)
function CheckIfExistThenHideBelow () {
var inElgbl = document.getElementById('banner');
if (typeof(inElgbl) != 'undefined' && inElgbl != null)
{
// How to check if below element inElgbl in doc?
var divsToHide = document.getElementsByClassName("below");
for(var i = 0; i < divsToHide.length; i++){
divsToHide[i].style.display = "none";
}
}
This is simpler than you think. Just loop over the target element's siblings that come after it and hide each by applying a class.
function removeSiblingsAfter(someElement){
// Check some element to see if it is currently being displayed as a block
if(getComputedStyle(someElement).display === "block"){
// Begin looping while there is a next sibling that is an element
while(someElement.nextElementSibling){
// Adjust the looping variable to be the next sibling
// so that the loop can eventually end
someElement = someElement.nextElementSibling;
someElement.classList.add("hidden"); // Hide each sibling
}
}
}
removeSiblingsAfter(document.getElementById("sampleTarget"));
.hidden { display:none; }
<div>Sibling Before<div>
<div id="sampleTarget">Target Element</div>
<div>Sibling After</div>
<div>
Sibling After
</div>
<div>Sibling After</div>

Javascript replace an element with a string

How can you change this
<div id='myDiv'><p>This div has <span>other elements</span> in it.</p></div>
into this
<div id='myDiv'>This div has other elements in it.</div>
hopefully using something like this
var ele = document.getElementById('myDiv');
while(ele.firstChild) {
replaceFunction(ele.firstChild, ele.firstChild.innerHTML);
}
function replaceFunction(element, text) {
// CODE TO REPLACE ELEMENT WITH TEXT
}
You can use innerText and textContent if you want to remove all descendant nodes, but leave the text:
// Microsoft
ele.innerText = ele.innerText;
// Others
ele.textContent = ele.textContent;
If you only want to flatten certain ones, you can define:
function foldIntoParent(element) {
while(ele.firstChild) {
ele.parentNode.insertBefore(ele.firstChild, ele);
}
ele.parentNode.removeChild(ele);
}
should pull all the children out of ele into its parent, and remove ele.
So if ele is the span above, it will do what you want. To find and fold all the element children of #myDiv do this:
function foldAllElementChildren(ele) {
for (var child = ele.firstChild, next; child; child = next) {
next = child.nextSibling;
if (child.nodeType === 1 /* ELEMENT */) {
foldIntoParent(child);
}
}
}
foldAllElementChildren(document.getElementById('myDiv'));
If you're not opposed to using jQuery, stripping the HTML from an element and leaving only the text is as simple as:
$(element).html($(element).text());
You can just take the innerText
console.log(ele.innerText)

Categories

Resources