JavaScript innerHTML manipulation with "onclick" handler - javascript

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>

Related

Removing selected child from an div element

My question aims to find the simplest way for removing a child from a div element. In this case either Apple, Orange or Banana. Which means you can put the cursor on one of these words and then click delete child.
The only way Iam thinking is doing it in two steps:
Returning the index of the selected child with returnIndexOfChild() (custom built)
Using the removeChild method
I mean when you are having a big text java script engine has to loop through the whole text to find the index. Is there a more direct way like deleting anchorNode by selection object?
//html part
<div contentEditable="true" id="editableField"><b>Apple</b><i>Orange</i><b>Banana</b></div>
<button onclick="deleteChild()"> Delete</button>
//javascript part
var selection = document.getSelection();
myDiv = document.getElementById("editableField");
//Returns index of selected child (1)
function returnIndexOfChild(){
let i = 0;
for (i; i < myDiv.childNodes.length; i++){
if(selection.anchorNode.parentElement === myDiv.childNodes[i]){
console.log(i);
break;
};
}
return i;
}
//Removing Child(2)
function deleteChild (){
myDiv.removeChild(myDiv.childNodes[returnIndexOfChild()]);
}
You can just call remove() on the element itself
function deleteChild (){
const selection = document.getSelection();
selection.anchorNode?.parentElement?.remove();
}
<div contentEditable="true" id="editableField"><b>Apple</b><i>Orange</i><b>Banana</b></div>
<button onclick="deleteChild()"> Delete</button>
2 solutions:
//html part
<div contenteditable="true" id="editableField">
<li>Apple</li>
<li>Orange</li>
<li>Banana</li>
<li>potato</li>
<li>grape</li>
<li>egg</li>
<li>milk</li>
</div>
<input type="text" class="input" />
<button class="button">Delete</button>
<script>
const myDiv = document.querySelector("#editableField");
const input = document.querySelector(".input");
const btn = document.querySelector(".button");
// first way thanks to event bubbling
myDiv.addEventListener("click", (ev) => {
ev.target.remove();
});
// second way, by iterating through items
btn.addEventListener("click", () => {
// The "onclick="..." in the html is old solution, new and better is addEventListener(...)"
const myDivChildren = [...myDiv.children]; // The "..." is spread operator, "myDiv" is HTMLCollection, an array like object in orter ot iterate ovet it I created array by spreading it
const inputValue = input.value || myDivChildren.length - 1; // take input value if exist, if not then last index so remove last item
myDivChildren.forEach((el, i) => {
if (i == inputValue) {
el.remove(); // When indexes match then delete
}
return; // When indexes match then stop executing loop
});
input.value = "";
});
I got rid of getSelection(), it complicates removing items, do you care about using getSelection()? Should I use it in the answer?

How to access a <p> inside three layers of <div> using 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>

How to select the values of the value attribute?

I am trying to hide a comment with its children by a toggling function, but I want to hide them based on the value attribute.
this is my js function:
function toggle(id, lft, rgt) {
var kids = (rgt - lft - 1) / 2;
if (kids >= 1) {
var element = document.querySelectorAll("div.md#com" + id)[0].getAttribute('value');
var low = Number(element.split('-')[0]);
var high = Number(element.split('-')[1]);
for(var i = low + 1; i <= high - 1; i += 1){
var x = document.querySelectorAll("div.md#com" + i)[0]
if (x.style.display === "none") {
x.style.display = "block";
} else {
x.style.display = "none";
}
}
}
and this is the result:
<div>
<div id="com4" class="md" value="7-10">yellow
<a onclick="toggle(4, 7, 10)" href="javascript:void(0)">[-]</a>
</div>
<div id="com5" class="md" value="8-9">not collapsing</div> //because of the id but I want to toggle them based on the value attribute
<div id="com8" class="md" value="10-11">collapses</div>
</div>
but I want:
<div>
<div id="com4" class="md" value="7-10">yellow
<a onclick="toggle(4, 7, 10)" href="javascript:void(0)">[-]</a>
</div>
<div id="com5" class="md" value="8-9">Should collapse</div>
<div id="com8" class="md" value="10-11">Should not collapse</div>
</div>
Assuming that the behavior you want is that when the <a> element is clicked on, you want to hide the elements based on the numeric range provided in its parent's value attribute. E.g. in your example, you will want to hide #com8 because it lies within the range of 7-10, but not #com5.
To achieve that, there are some changes that I would strongly recommend doing:
Give all your comments a more unique class, e.g. .com. I suspect that .md is a very generic class that you might use in other elements on the same page, so let's just get that over with.
Do not use a reserved value attribute, since that is reserved for input-like elements. Use data-* attributes instead: for example, we can use data-range="7-10". Even better, you can use data-min and data-max, so that we can avoid parsing the range. I'll leave the latter up to you.
Do not use inline JS, but rely on addEventListener to bind click events to your <a> element
Now, to the solution: if we slightly tweak your logic, it is actually very doable:
When the <a> element is clicked on, get the data-range value of its closest parent. This can be done using .closest('div.com'), and then use the dataset API to retrieve the range.
Split the data-range attribute, and remember to convert it into a number by using the unary +
Use ES6 array destructuring to assign min and max variables to the parsed data-range
Go through all .com elements:
If the element matches our trigger element's parent element, we ignore it
Otherwise, we check if its id—parsed using String.prototype.substring() to strip out the first 3 characters—lies inside or outside the numeric range
Then, use this boolean value to determine the style.display property of the element
See proof-of-concept below:
const collapseButtons = document.querySelectorAll('.collapse');
Array.from(collapseButtons).forEach(collapseButton => {
collapseButton.addEventListener('click', e => {
e.preventDefault();
// Use ES6 destructuring to assign min/max values
const [min, max] = e.target.closest('div.com').dataset.range.split('-').map(v => +v);
const comments = document.querySelectorAll('.com');
Array.from(comments).forEach(comment => {
// If the element matches the trigger element's parent, do nothing
if (comment === e.target.closest('div.com')) {
return;
}
// Otherwise, decide if we should hide or show it
const id = +comment.id.substring(3);
const shouldHide = id < min || id > max;
comment.style.display = shouldHide ? 'none' : 'block';
});
});
});
<div>
<div id="com4" class="com md" data-range="7-10">yellow
<a class="collapse" href="#">[-]</a>
</div>
<div id="com5" class="com md" data-range="8-9">Should collapse</div>
<div id="com8" class="com md" data-range="10-11">Should not collapse</div>
</div>
Try this instead
<div class="box" name="box1"></div>
<div class="box" name="box2"></div>
<div class="box" name="box3"></div>
$('.box').on('click', function(){
var value = $(this).attr('name');
alert(value);
});
Or if you want to use value attribute
$('.box').on('click', function(){
var value = $(this).attr('value');
alert(value);
});

Identify cloned DOM element

I am using MediumEditor which a WYSIWYG editor using contenteditables. I need to assign different IDs to each element inside of the contentEditable, but (when you press Enter) the editor will clone a paragraph from an esiting one with all it's attributes. I am wondering if there is a way to identify the new <p> element from the one it was cloned from? The new element can be placed either before or after the existing one.
UPDATE:
Here's an example:
https://jsfiddle.net/wwoh7e62/
<div id="container">
<p id="myid" class="myclass" data-id="myid">some text</p>
</div>
<button onclick="doClone(); myFunc();">clone</button>
<script>
doClone = function() {
var container = document.getElementById('container');
var node = container.getElementsByTagName('p')[0].cloneNode(false);
node.innerHTML = 'cloned node';
container.appendChild(node);
}
myFunc = function () {
console.log('my func');
}
</script>
The code in doClone I don't have access to. My code should reside in the myFunc function.
While I was typing the fiddle I realized that the solution will probably be in attaching an event listener (which is not cloned) and the new node will be the one that does not have the event listener.
UPDATE:
ids that were assigned previously need to stay the same as thay are used to identify particular nodes.
You can try this :
Remove the id from "p"
<div id="container">
<p class="myclass" data-id="myid">some text</p>
</div>
<button onclick="doClone(); myFunc();">clone</button>
Then update them into your Func function :
var length = document.getElementById("container").getElementsByTagName("p").length;
for(var i=0; i < length; i++) {
document.getElementById("container").getElementsByTagName("p")[i].id = i;
}
Does this help ?

onClick replace javascript element, with new javascript element

What I am trying to figure out is how to make a button, that when you click, will replace itself with a textbox. I found this code on the W3Schools website, and can't figure out how to put javascript (or HTML) elements in.
<p>Click the button to replace "Microsoft" with "W3Schools" in the paragraph below:</p>
<p id="demo">Visit Microsoft!</p>
<button onclick="myFunction()">Try it</button>
<script>
function myFunction() {
var str = document.getElementById("demo").innerHTML;
var res = str.replace("Microsoft", "W3Schools");
document.getElementById("demo").innerHTML = res;
}
</script>
</body>
</html>
<input type="text" name="textbox" value="textbox"><br>
In the end I want to be able to replace a button with the textbox I put outside the html tags
I would not suggest you the innerHTML replacement method.
Here are the steps where you can use replaceChild
Get the parent node of the selected element
use replaceChild
Here the code
// create the new element (input)
var textBox = document.createElement("input");
textBox.type = "text";
// get the button
var button = document.getElementById("demo");
// reference to the parent node
var parent = element.parentNode;
// replace it
parent.replaceChild(textBox, button);
On older browser you probably need a slighlty different solution.
var parent = button.parentNode;
var next = button.nextSibling;
// remove the old
parent.removeChild(button);
// if it was not last element, use insertBefore
if (next) {
parent.insertBefore(textBox, next);
} else {
parent.appendChild(textBox);
}
I ended up finding a code that works off of http://www.webdeveloper.com/forum/showthread.php?266743-Switch-Div-Content-Using-Javascript

Categories

Resources