Continuously changing id of div based on button click - javascript

I need to change the ID of a div based on the buttons being clicked. This is what my buttons look like:
var id; // global variable
holding = "holder";
var counter = 1;
function sendData(valueId) {
id = valueId;
if (counter <= 1) {
document.getElementById(holding).setAttribute("id", id);
counter++;
holding = id;
} else {
document.getElementById(holding).setAttribute("id", id);
holding = id;
}
console.log(holding);
console.log(id);
}
<button id="republic" onclick="sendData('republic')">Republic</button>
<button id="ndtv" onclick="sendData('ndtv')">NDTV</button>
<button id="cnnnews18" onclick="sendData('cnnnews18')">CNN</button>
My div to be targeted is just an empty, <div id="holder"></div>. However, when I try to run this I see the div change the first time but it remains on the same ID for every other click. How can I fix this?

The trouble is that when you press one of the buttons, you're creating multiple elements with the same id. The "holder" div will be assigned the id of one of the buttons.
Ids are meant to be unique. When this isn't the case, the behavior of getElementById is undefined, so the browser likely just returns the first element it finds that qualifies. In your example, this will be one of your buttons, rather than the "holder" div.
To get the code to work, make sure the ids of your buttons and the valueId variables in your code are different.
Bladeski's suggestion to use
const holding = document.getElementById('holder');
is great. But you should also make sure the ids in your document stay unique, just to conform to the HTML standard and avoid any other undefined behavior.

Create a reference to the 'holding' div in a constant and the use this for changing the attribute. For example, at the start of the code:
const holding = document.getElementById('holder');
Then, whenever you need to update the element, use:
holding.setAttribute(...);
This also happens to be more efficient as it means that the DOM is not being queried every time the element needs to be accessed since the holding constant provides a direct reference to it.

You need to remove the ids from the buttons.
<button onclick="sendData('republic')">Republic</button>
<button onclick="sendData('ndtv')">NDTV</button>
<button onclick="sendData('cnnnews18')">CNN</button>
The first click only one element with id 'holder' would be available in dom. But in the next we would have two since both div and button would have republic/ndtv/cnnnews18 and getElementById returns the button and this goes on.
But instead of this having the reference holder div at root would be a better approach. The code would be:
var id; // global variable
holding = document.getElementById('holder');
var counter = 1;
function sendData(valueId) {
id = valueId;
if (counter <= 1) {
holding.setAttribute("id", id);
counter++;
} else {
holding.setAttribute("id", id);
}
console.log(id);
}

Related

Unable to change the class of a div using JavaScript

So I got into JavaScript and tried setting up the following scenario:
I have 2 Buttons on my Site (IDs are buttonWebdev and buttonUXUI), which should trigger an Action when they are hovered upon. If buttonWebdev is hovered upon, it should hide all p', h3's and imgs with the class "classWeb". I wrote this code to do it, but it doesn't work:
HTML:
<h3 class="classWeb">Editierbare Inhalte</h3>
<p class="classWeb">Test</p>
<button class="buttonImg" id="buttonWebdev"><img src="./img/buttonWebdev.png" /></button>
<script type="text/javascript">
var button = document.getElementById('buttonWebdev');
var classWeb = document.getElementsByClassName('classWeb');
button.onmouseover = function() {
classWeb.className = 'webdev';
}
CSS:
.classWeb.webdev {
display: none;
}
First, since there can be more than one element with a given class on a page, getElementsByClassName returns a list of elements instead of a single element. You’ll need to perform your action on every element of that list, with a for…of loop, for example:
for (let element of classWeb) {
element.className = 'webdev';
}
(for…of is relatively new, though, so you might have to use a regular for loop depending on your target browsers.)
After fixing this, you’ll run into another problem. When you assign to className like that, you’re setting the entire list of classes on an object. If the list of classes is 'webdev', it no longer includes 'classWeb'. Modern browsers support an API to add a class without affecting the rest:
for (let element of classWeb) {
element.classList.add('webdev');
}
The way to diagnose these sorts of problems is by opening up your browser’s developer tools, looking for JavaScript errors in the console, and looking at the state of the elements you’re trying to affect in the document tree.
document.getElementsByClassName('classWeb'); this gives collection & to add classes you need to iterate over them & then apply classes.
classWeb[0].className = 'webdev'; would reset class
either use classWeb[i].className += ' webdev'; or classWeb[i].classList.add('webdev');
See below working example
var button = document.getElementById('buttonWebdev');
var classWeb = document.getElementsByClassName('classWeb');
button.onmouseover = function() {
for (var i = 0; i < classWeb.length; i++)
classWeb[i].className += ' webdev';
}
.classWeb.webdev {
display: none;
}
<h3 class="classWeb">Editierbare Inhalte</h3>
<p class="classWeb">Test</p>
<button class="buttonImg" id="buttonWebdev">hover over me</button>
Firstly, the
document.getElementsByClassName('classWeb');
will give you a LIVE list of all the matched elements. That means that when you reassign the class like so:
classWeb[0].className = 'webdev';
the element will be removed from the list, as it no longer corresponds to the original command which was to find all elements with a specific class (which you overrode with 'webdev').
An easier and more friendly api is querySelectorAll which mimics the jQuery selector (which uses css selectors to find elements, thats why there is a # for an id and a . for a class name). The example below shows, how to use it.
var button = document.querySelector('#buttonWebdev');
var classWeb = document.querySelectorAll('.classWeb');
button.onmouseenter = function() {
for (var i = 0; i < classWeb.length; i++) {
classWeb[i].className = 'webdev';
}
}
ps. The querySelectorAll is not a live list, so items will not disappear after you change their class.
ps2. Use onmousenter instead of onmouseover as the onmouseenter is only called when the mouse starts hovering over an element, while onmouseover will be called on every mouse move over the element (even if already hovering).
Good luck!

addEventListener will not attach a listener to the element

/***************************************************** *
* Function: renderTodos
* Builds a list of todo items from an array
* rebuilds list each time new item is added to array
****************************************************** */
function renderTodos(arr) {
// erases all previous content
uList.innerHTML = '';
for ( var i = 0; i < tdArray.length; i++ ) {
// create a link element
var remex = document.createElement('span');
//link element didn't work so try a button
//var remex = document.createElement('input');
//remex.setAttribute('type','button');
remex.setAttribute('class', 'deletex');
remex.innerHTML="X";
// set attribute index and value
remex.setAttribute('index', i);
remex.addEventListener('click',removeTodo);
console.dir(remex);
// create <li> element
var li_element = document.createElement('li');
li_element.style.lineHeight = "20pt";
li_element.appendChild(remex);
li_element.innerHTML += tdArray[i];
// add item to list
uList.appendChild(li_element);
inField.value = '';
}
} // /renderTodos
This function builds a list based on text field inputs. Each time the the "add item" button is clicked, the event calls this function to add the item to the list. Everything works beautifully UNTIL I try to add the eventListener to the "x" that is appended to the li element prior to the list item text. The idea is that the "x" is clickable, and onClick it removes the list item entry from the list. But I have tried 6 ways to Sunday to attach an eventListener to the "x" object and nothing works. I have tried attaching the event listener to a span object, and a button object; I have moved "remex.addEventListener..." all around in the function, after it has been rendered, before it gets rendered, etc.; I have eliminated the CSS; I have tried changing the addEventListener to onClick; I have tried this code on our own Apache server, I have moved it to jsbin.com in hopes that some server setting was getting in my way; and probably a few more things I can't remember in the long list of things I have tried. As you see, I have tried it as a button and as a span, hence the commented code.
In short, no matter what I try, the eventListener will NOT attach to the "x". Any ideas? Do you need to see more of the code?
This line overrides the attached eventlistener:
li_element.innerHTML += tdArray[i];
Setting innerHTML replaces all the original elements within li_element. += is just a shortcut to li_element.innerHTML = li_element.innerHTML + tdArray[i];
If tdArray[i] contains just some text, you can add its content like this:
li_element.appendChild(document.createTextNode(tdArray[i]));
If tdArray[i] contains elements, you could append a wrapper element, and then set the innerHTML of the wrapper.

Changing the ID of html elements

I have a form which allows user to add elements(which might be a field set or a text box) dynamically. I'm able to assign a new ID to the elements when added but I'm not able to make it in a sequence as the user can add elements in between as well.
So for example, there is an Id named XXX1 and the user adds a new element after it which is xxx2. Now if the user adds a new element again after XXX1, it comes up as XXX3. So the order of the elements is XXX1, XXX3, XXX2. I'm not able to control the names when it is being added. So I'm trying to re-assign the names after add.
I'm trying to get all elements in an array and change the ID as follows
document.getElementById('xxx3').setAttribute('id', 'xxx2');
But this doesn't work as ID XXX2 already exists for another element. Please help me with a solution for this.
So why not change the ID of xxx2 first, to move it out of the way, then putting it back in place later?
document.getElementById('xxx2').setAttribute('id', 'temporaryId');
document.getElementById('xxx3').setAttribute('id', 'xxx2');
document.getElementById('temporaryId').setAttribute('id', 'xxx3');
Why not go from the last element to the first(the one's after the position you are inserting to) and add one to the ID?
for example:
var insertAt = 2; // for an element to be called xxx2
var els = [...]; // an array with all of the elements
if(els.length >= insertAt){
for(var i = els.length-1; i >= insertAt-1; --i){
els[i].setAttribute('id', 'xxx'+(i+2));
}
}
// Add the new element here which will be called xxx2

Why create loops to get elements for events?

The textbook I'm using to learn JavaScript uses the following code to display an alert dialogue whenever a user clicks on a paragraph:
var paras = document.getElementsByTagName("p");
for (var i=0; i<paras.length; i++) {
paras[i].onclick = function() {
alert("You clicked on a paragraph.");
}
}
I don't see the reason to loop through all the p elements, but instead identify them and simply attach the onclick event handler to it. Like this:
var paras = document.getElementByTagName('p');
paras.onclick = alert("You clicked on a paragraph.");
Doesn't that do the same thing? Why is it necessary to loop through the p elements?
No, that absolutely does not do the same thing:
You simply cannot attach event handlers to an HTML node list (well, you can, but nothing will happen);
You're trying to set the "onclick" to the result of the alert() statement, not a function as in your example. (Doesn't really matter because it won't work anyway.)
Now there is a way to handle the clicks with just one event handler, but I'll let you keep reading your book :-)
edit — To elaborate on point 2, this:
alert("hi");
is a function call. Its value will be whatever is returned from calling that function. Thus,
paras.onclick = alert("You clicked on a paragraph.");
sets the "onclick" property of the object that "paras" refers to, and it sets it to the value returned from alert() (which is probably always undefined).
As in the sample code from your book, things like "onclick" handlers need to be functions. That's what's going on in the middle of your sample code: the "onclick" property of each individual <p> DOM element is being set to a function. Inside that function is the call to alert().
Suppose you have
​<p>​First Paragraph</p>
<p>Second Paragraph</p>
<p>Third Paragraph</p>
So var paras = document.getElementsByTagName("p"); will return a collection of p like
[<p>​First Paragraph​</p>​, <p>​Second Paragraph​</p>​, <p>​Third Paragraph​</p>​]
If you write paras.onclick = alert("You clicked on a paragraph."); then it won't work because paras is an array of some p elements not the p element itself and only an html element has an event, so you have to loop through the collection and add event handler for each p element individually.
Even if you have only one p then it will return an array with one p element inside it, i.e.
​<p>​First Paragraph</p>
And var paras=document.getElementsByTagName("p"); will return[<p>​First Paragraph​</p>​]
So to add an event handler you can simply write
paras[0].onclick = function() { // 0 is the first element in the collection
alert(this.innerHTML);
}
Here is an example, I hope it'll help you to understand the process.
No, that will not work.
The function document.getElementsByTagName will return you an array of nodes because several nodes can have the same name (hence the plural for "Elements") : you can have several paragraph in you page, and this function will return them all. Even if you have only one paragraph, you will get an array containing one single element.
On the other hand, you could select the node with its ID by using the document.getElementById function. In this case, the result will be a single node, because IDs are supposed to be unique within a document.
var para = document.getElementByTagId('myParagraph');
paras.onclick = function(){ alert("You clicked on a paragraph.") };

Toggling elements with JavaScript

I want to toggle a div element (expand/collapse) when clicked.
I have many div elements, on click to new element, I want to collapse the previous one and expand the current clicked one.
I tried using static type variable to save the instance of previous div tag and compared with the current selection, but I don't know why is it not working.
Searching about this, I got similar code idea to collapse all div and then expand the current selected only, but I want to just toggle the previous one with new one, not collapse all div and expand the selected (though I would be using it if other way is not possible)
Can it be done using static variables of js?
At its simplest, you can simply do something like this:
var divs = document.getElementsByTagName('div'),
collapseClass = 'collapsed',
current = divs[0];
// Hide all elements except first, add click event hander
for(var i = 0, len = divs.length; i < len; i++){
divs[i].onclick = function(){
if(this !== current){
toggle(this, current);
current = this;
}
};
if(i > 0) toggle(divs[i]);
}
This will store the current element in a variable, then toggle it when another element is clicked. It also uses an if statement to check if the currently clicked element is the one currently visible element, and only toggles if its not.
See a working demo of this here: http://jsfiddle.net/GaxvM/
You can assign a unique ID to each of the elements and use document.getElementById to identify both elements, and then collapse one/expand the other.
If you number them sequentially (like div1, div2, div3, etc) you could do something like:
function colexp(div_id){
div_2_collapse = document.getElementById(div_id);
next_div = div_id.substr(0,3) + parseInt(div_id.substr(3))+1;
div_2_expand = document.getElementById(next_div);
hide(div_2_collapse);
show(div_2_expand);
}

Categories

Resources