How to access a <p> inside three layers of <div> using Javascript? - javascript

I'm trying to use Javascript so that a paragraph will alternate between two texts when the user presses a button on the webpage. The problem is that the <p> element I'm trying to manipulate lies within a <div> within a <div> within a <div>.
A low-level mockup of my code can be seen below:
<div id="div1">
<div id="div2">
<div id="div3">
<p>text1</p>
</div>
</div>
</div>
All solutions I've found only state what to do if the <p> is within one <div>; solutions which have not worked for me.
Here's my latest attempt:
function translate() {
var x = document.getElementById("div1").getElementById("div2").getElementById("div3").getElementsByTagName('p')[0];
if (x.innerHTML === "text1") {
x.innerHTML = "text2";
} else {
x.innerHTML = "text1";
}
}
How would I get this to work?
Edit: Nothing seems to be working so far. The <div> element all have classes, but that shouldn't affect things, right?
Edit2: My apologies for taking up your time, but I've found the issue was something else entirely. I'd been using a reserved word for my function call this whole time and had somehow never noticed that that was the issue instead. Thanks again to those who answered, and I shall look deeper at my code before posting my next question.

Why not just use
var x = document.getElementById("div3")
If accessing by the Id directly, it does not really matter what the other DIVs are.

If divs are always in that order, you can do it like this:
var x = document.querySelector('#div1 #div2 #div3 p');

You can use document.querySelectorAll to get an array of all p tags on the page.
const pTags = document.querySelectorAll('p')
pTags.forEach((p, i) => {
if(i % 2 === 0) {
p.innerHTML = 'text1'
} else {
p.innerHTML = 'text2'
}
})

Instead of Ids, you can be more specific & try the following:
var x = document.querySelector('div > div > div > p')
This will be a very strict implementation & follows the condition without the ids or classes.

Use querySelector in order to use the CSS selector #div3 p which means "pick up the paragraph element that is inside the element that has a div3 id".
Then, as you're just changing a string of text, change either the textContent of that element, or its innerText (there are subtle differences between them.)
const para = document.querySelector('#div3 p');
const button = document.querySelector('button');
button.addEventListener('click', translate);
function translate() {
if (para.textContent === 'text1') {
para.textContent = 'text2';
} else {
para.textContent = 'text1';
}
}
<div id="div1">
<div id="div2">
<div id="div3">
<p>text1</p>
</div>
</div>
</div>
<button type="button">Translate</button>

Related

JavaScript innerHTML manipulation with "onclick" handler

I wanna know how I can change the innerHtml to switch back and forth between two states.
I have this html
<div class="test" id="test">
<p>this is a test</p>
</div>
<p id="js" class="test" >Change</p>
and this is the JavaScript I have
let button = document.getElementById("js");
button.onclick = function() {
document.getElementById("test").innerHTML = "test";
};
how can I change the innerHTML from "test" to "another test" and vice versa ?
It's better not to store state in the HTML - don't test against what's currently in the HTML, keep a variable in your Javascript instead.
You should also only use let when you want to warn other programmers that you're going to reassign the variable in question - otherwise, use const.
Also, if you're changing the text of an element, assign to textContent instead - it's safer, faster, and more predictable.
const button = document.getElementById("js");
const test = document.getElementById("test");
let clicked = false;
button.onclick = function() {
clicked = !clicked;
if (clicked) test.textContent = 'another test';
else test.textContent = 'test';
};
<div class="test" id="test">
<p>this is a test</p>
</div>
<p id="js" class="test" >Change</p>
First, .innerHTML is only for when you are setting/getting a string that contains HTML. When you are not, use .textContent, which is more efficient and reduces security risks.
Then, you only need a toggling if statement, which can be done with a JavaScript ternary operator.
Also, rather than using event properties, like onclick. Use .addEventListener(), which is more robust and follows the modern standard.
// Get a reference to the <p> that is inside of the element who's id is "test"
let output = document.querySelector("#test p");
// Set up an event handler for the "Change" element
document.getElementById("js").addEventListener("click", function() {
// Check the current textContent and set it to the other value
output.textContent === "test" ? output.textContent = "another test" : output.textContent = "test";
});
<div class="test" id="test">
<p>this is a test</p>
</div>
<p id="js" class="test" >Change</p>
Just toggle between the two with an if statement like this:
let button = document.getElementById("js");
let toggleText = document.getElementById("test");
button.addEventListener('click', function() {
if (toggleText.innerHTML !== "another test") {
toggleText.innerHTML = "another test";
} else {
toggleText.innerHTML = "test";
}
});
ALso, as #CertainPerformance mentioned in the other answer, I would recommend that you use textContent rather than innerHTML since you're checking and toggling the element's string content and not the element itself.
I'm going to expand on a couple of excellent answers here.
What if you had more options and wanted to iterate through a list?
In that case, I'd use a data attribute to hold the button state. This is still a valid approach with just two options.
I am going to use Scott's answer as the basis of mine, so everything he says is pertinent.
// Get a reference to the <p> that is inside of the element who's id is "test"
let output = document.querySelector("#test p");
// Set up an event handler for the "Change" element
document.getElementById("js").addEventListener("click", function() {
//Here is out list of options
var options = ["test", "another test", "yet another test", "Really? Another Test?"];
//get the current state value and increment it
var index = parseInt(this.dataset.state, 10) + 1;
//if index is out of bounds set it to 0
if(index > options.length -1 || index < 0)
{
index = 0;
}
//Set the text
output.textContent = options[index];
//Set the new state
this.dataset.state = index;
});
<div class="test" id="test">
<p>this is a test</p>
</div>
<p id="js" class="test" data-state="-1" >Change</p>

Select div with a text inside in JS

I want to modify a div with a special text inside like this.
<div>
<p>
A global issue
</p>
</div>
How can I get it in JS without using id or class ? And only the div with the text "A global issue".
Is there a way to do it?
To target the div and set it to display: none you can run either:
// Pure JS
document.querySelector("div p").style.display = "none"
// w/jQuery
$('div p').hide();
If there's more then one div p tags in your HTML, you can also search by text using the following:
$('div p:contains("A global issue")').css('display', 'none');
If you want to use simple javascript solution, see snippet below
First, get all divs from page
Second, store your search text in a variable
Third, loop through all divs and find the one containing your text, then you can do whatever you want with it. I added a backgroundColor red to it
var divs = document.querySelectorAll("div");
var myText = "A global issue";
var i;
for (i = 0; i < divs.length; i++) {
if (divs[i].textContent.indexOf(myText) > 0 ) {
divs[i].style.backgroundColor = "red";
}
}
<div>
<p>
A global issue
</p>
<p>
More text here
</p>
</div>
<div>
<p>
Not a good text
</p>
</div>
You can use jquery:
$('div:contains("A global issue")').css('background-color', 'red');
<div>A global issue</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
let i=5;
let divs =Array.from( document.querySelectorAll('div > p')); // it returns array
divs[i].style.display= "none";
Edit:
for(let i=0;i<divs.length;i++){ if(divs[i].textContent==="some text"){ divs[i].style.display="none"; } }
if you want to change parent node do divs[i].parentNode.style.display="none"

Display none to All div by loop and display a perticular div which I want to show

I am using Javascrip and I have a function like the following where I want to hide all div. But I don't know why this code is not working. Will anyone help me with this?
Javasvript
function showDiv(divTag,id)
{
var i;
for(i=1;i<7;i++)
{
document.getElementById(divTag+i).style.display = 'none';
}
document.getElementById(divTag+id).style.display = 'block';
}
or
function showDiv(divTag,id)
{
var i;
for(i=1;i<5;i++)
{
var tempDiv = divTag + i;
document.getElementById(tempDiv).style.display = 'none';
}
document.getElementById(divTag+id).style.display = 'block';
}
And HTML
Show Only Div1
<div id="hide_1">
Abc
</div>
Show Only Div2
<div id="hide_2">
BCD
</div>
Show Only Div2
<div id="hide_3">
EDF
</div>
Show Only Div2
<div id="hide_4">
FGE
</div>
Both of the abov process I have tried but failed to do that
Several things:
The "onclick" (not "onClick") is the correct way to assign the click event handler in both html and JavaScript.
You are looping from 1 to 6 in for(i=1;i<7;i++) line of the first function, but you only have 4 elements in your html. When reaching the non-existing fifth - your code will throw an error. Something along the lines of "TypeError: Cannot read property 'style' of null".
As #verisimilitude has mentioned, you have a syntax error in your html where you put a quoted text inside another text that's quoted in the same way. It should be onclick="showDiv('hide_',1)". Note the single quotes around 'hide_'.
Here's the code that works. Click here to see it in action.
Here's your JavaScript function:
// Please note that it must be in the global scope
// otherwise you won't be able to call it from your html.
function showDiv(divTag, id) {
var i;
for (i = 1; i < 5; i++) {
var tempDiv = divTag + i;
document.getElementById(tempDiv).style.display = 'none';
}
document.getElementById(divTag+id).style.display = 'block';
}
Also, take a look at another working version of your code that pre-validates the existence of your elements before hiding/showing them. So you don't have to worry about your for loop iterating over elements that have been removed.
And here's your html:
Show Only Div1
<div id="hide_1">Div1</div>
Show Only Div2
<div id="hide_2">Div2</div>
Show Only Div3
<div id="hide_3">Div3</div>
Show Only Div4
<div id="hide_4">Div4</div>​
Show Only Div1
yields a javascript syntax error. This should be
Show Only Div1
Check the single quotes around "hide_"

javascript selectors

How does one select DOM elements in javascript?
Like for example:
<div class="des">
<h1>Test</h1>
<div class="desleft">
<p>Lorem Ipsum.</p>
</div>
<div class="Right">
<button>Test</button>
</div>
</div>
Now how do i select h1? This is just a part of a bigger Page, so cannot use getElementsByTagName(), since others might get selected. Also since there might be other h1's in the document later, i cannot attach the index(body's) to above.
Is there a simple way to select, say <h1> tag which is under the classname of desleft?
I cannot use jQuery or any other libraries.
You can use this to get to your H1:
var des = document.getElementsByClassName('des')
var fc = des[0].getElementsByTagName('h1')
alert(fc[0].innerHTML)
w3.org has selectors now (http://www.w3.org/TR/selectors-api/#examples). Here are 2 different ways that worked for me on Chrome. You may want to use querySelectorAll function that returns a list.
<script type="text/javascript">
//looks for <h1> tag under <div> with className "des"
showOff1 = function() {
var x = document.querySelector(".des h1");
alert(x.innerHTML);
}
//looks for <div> tag with className "desleft" and then use previousSibling to traceback <h1> tag
showOff2 = function() {
var y = document.querySelector("div.desleft");
var z = y.previousSibling.previousSibling;
alert(z.innerHTML);
}
</script>
<body onload="showOff2();">
Use querySelectorAll
You can use querySelectorAll:
// Will return a NodeList even if there is only one element found
var heading = document.querySelectorAll('.des > h1');
heading[1].style.color = 'red'; // NodeList is similar to an array
This will return a NodeList.
or
Use querySelector to return the first element found:
var first_heading = document.querySelector('.des > h1');
first_heading.style.color = 'blue';
Commonly used with an id selector #single-header-id.
Here's a demo
getElementsByTag()
Would be a function that you can start with, and then you can filter for the DOMElements that have the class.
var h1_array = document.getElementsByTag('h1');
var h1_class_array = [];
for (var i=0, len=h1_array.length; i < len; i++) {
if (h1_array[i].className.indexOf('classname') !== -1) {
h1_class_array.push(h1_array[i]);
}
}
The .indexOf function returns -1 if the needle is not found in the haystack.
Now re-reading your question, why not just give your h1's id's ?
DOM traversal is one of javascript's glaring issues (enter jQuery).
a simple getElementById() would save you a headache, and ids on all your h1's would be much cleaner in the end than trying to formulate an algorithm to select them by other means.
If you mean to select a h1 that is before the first element of class desleft, you could always do this:
document.getElementsByClassName("desleft")[0].previousSibling.previousSibling
Example: http://jsfiddle.net/Xeon06/ZMJJk/
previousSibling needs to be called twice because of the empty text node between the two. That's why using libraries to do this stuff is really the best way to go.
var h1 = document.querySelector('.desleft').previousElementSibling;
Find element with className='desleft' using selector '.desleft'
Just move back to previous element (not to previous node!)

Remove parent element after removing last child element

I have a list of elements on a page, for the sake of discussion we can say I have the following:
<div id="group_01">
<div id="entry_1-01">stuff x</div>
<div id="entry_1-02">stuff x</div>
</div>
<div id="group_02">
<div id="entry_2-01">stuff x</div>
<div id="entry_2-02">stuff x</div>
</div>
The delete link calls an Ajax request and deletes the entry, after a succesful Ajax call, the entry div is removed from the page. My question is:
How can I remove the containing group div once all of it's entries have been deleted?
I hope that's a detailed enough question. I feel like this isn't anything new, yet two days of search has resulted in nothing.
Before you delete the child element, get its parent, count the number of children, and then after deleting the child, delete the parent if the child count is zero. Here is a quicky piece of sample code:
function d (x)
{
var e = document.getElementById(x);
var p = e.parentNode;
p.removeChild (e);
if (p.childNodes.length == 0) {
var pp = p.parentNode;
pp.removeChild (p);
}
}
I added onclicks to your divs like this:
<div id="group_01">
<div id="entry_1_01">stuff 11<a onclick="d('entry_1_01');" href="#delete">x</a></div>
<div id="entry_1_02">stuff 12<a onclick="d('entry_1_02');" href="#delete">x</a></div>
</div>
I also changed the link to "#delete". You could tidy this up in various ways.
A function like this should would work:
function removeNodeIfEmpty(node) {
var container = document.getElementById(node);
var nodeCount = 0;
for (i = 0; i < container.childNodes.length, i++) {
if (container.childNodes[i].nodeType == 1) {
nodeCount += 1;
}
}
if (nodeCount < 1) {
container.parentNode.removeChild(node);
}
}
This should account for the whitespace issue.
Assuming you do something like this to remove an entry:
entryDiv.parentNode.removeChild(entryDiv);
then you should be able to use the following code to remove the group div when the last child is removed:
var groupDiv = entryDiv.parentNode;
groupDiv.removeChild(entryDiv);
if (!groupDiv.firstChild) {
groupDiv.parentNode.removeChild(groupDiv);
}
...although you need to watch out for whitespace-only text nodes, if these entries haven't been created directly by script.
Really depends what library you're using
http://docs.jquery.com/Traversing/parent#expr
should be a suitable expression

Categories

Resources