I'm having trouble understanding why I can't properly access firstChild of an element object in a JavaScript class. I can set innerHTML without the firstChild properly, but I'd like to set it on firstChild. Using console.dir(this.waitStatus) shows that it has a firstChild. I'm not using jQuery because it may not be loaded when I want this run, since it is a loading indicator.
class LoadingIndicator{
constructor(elementID){
this.tick = 8;
this.waitStatus = document.getElementById(elementID);
setInterval(
this.animateLoader.bind(this),
10
)
}
animateLoader (){
if(this.tick == 8){
this.waitStatus.firstChild.innerHTML = ".";
}
else if(this.tick == 16){
this.waitStatus.firstChild.innerHTML = "..";
}else if(this.tick == 24){
this.waitStatus.firstChild.innerHTML = "...";
this.tick = 0;
}
this.tick += 1;
}
}
var supervisorLoadingIndicator = new LoadingIndicator('supervisorsTableLoading');
html
<p id='supervisorsTableLoading' style='width:700px; height:0px; text-align:left; padding-bottom:20px;'>
<span id='supervisorsTableLoadingInner' style='margin-left:30%'> </span>
</p>
The firstChild is a text node (the line break before the <span), so .innerHTML isn't useful. Use .firstElementChild instead, or .children[0].
class LoadingIndicator {
constructor(elementID) {
this.tick = 8;
this.waitStatus = document.getElementById(elementID);
setInterval(this.animateLoader.bind(this), 10)
}
animateLoader () {
if (this.tick == 8) {
this.waitStatus.firstElementChild.innerHTML = ".";
} else if (this.tick == 16) {
this.waitStatus.firstElementChild.innerHTML = "..";
} else if (this.tick == 24) {
this.waitStatus.firstElementChild.innerHTML = "...";
this.tick = 0;
}
this.tick += 1;
}
}
var supervisorLoadingIndicator = new LoadingIndicator('supervisorsTableLoading');
Or you could simply get rid of that whitespace text and use .firstChild.
Also, you're not really setting HTML content, so I'd personally use .textContent instead.
this.waitStatus.firstElementChild.textContent = "...";
IE8 and lower don't support either of these properties.
If you're still supporting IE8, then you can polyfill them both.
If you're supporting IE6/7, then stick with .innerHTML and get rid of that whitespace.
use this.waitStatus.children[0], firstChild will return non element node.
class LoadingIndicator{
constructor(elementID){
this.tick = 8;
this.waitStatus = document.getElementById(elementID);
console.log(this.waitStatus.firstChild);
setInterval(
this.animateLoader.bind(this),
10
)
}
animateLoader (){
if(this.tick == 8){
this.waitStatus.children[0].innerHTML = ".";
}
else if(this.tick == 16){
this.waitStatus.children[0].innerHTML = "..";
}else if(this.tick == 24){
this.waitStatus.children[0].innerHTML = "...";
this.tick = 0;
}
this.tick += 1;
}
}
var supervisorLoadingIndicator = new LoadingIndicator('supervisorsTableLoading');
<p id='supervisorsTableLoading' style='width:700px; height:0px; text-align:left; padding-bottom:20px;'>
<span id='supervisorsTableLoadingInner' style='margin-left:30%'> </span>
</p>
Related
I am trying to target span tags by class name to change the color based on respective class, but I'm not seeing them in my DOM, all I see is the <p> tag. The text I'm adding to the span tags in my loop is working, but there are no span tags for me to target.
I'm expecting <span> tags to be nested in the <p> tag so I can target them to change the color.
Here's the code...
<p class="holder"></p>
const holder = document.querySelector('.holder');
const span = document.createElement('span');
const body = document.querySelector('body');
const html = document.querySelector('html');
const fizzBuzz = document.getElementsByClassName('.fizzBuzz');
html.style.backgroundColor = 'gray';
for(let i = 1; i <= 100; i++) {
if (i % 3 === 0 && i % 5 === 0) {
span.setAttribute('class', 'fizzBuzz');
fizzBuzz.style.color = 'brown';
span.textContent = 'FizzBuzz, ';
holder.appendChild(span);
} else if (i % 5 === 0) {
span.setAttribute('class', 'buzz');
span.textContent = 'Buzz, ';
holder.appendChild(span);
} else if (i % 3 === 0) {
span.setAttribute('class', 'fizz');
span.textContent = 'Fizz, ';
holder.appendChild(span);
} else {
holder.textContent += `${i}, `;
holder.style.color = 'white';
}
if (i === 100) {
holder.textContent = holder.textContent.slice(holder.textContent, holder.textContent.length - 2);
holder.textContent += '.';
}
}
body.appendChild(holder);
Also, this is my first question and I'm new to coding, so let me know how I can improve my question or code, thank you
holder.textContent += '.';
Assigning a string to textContent erases the existing content of the element, including the span element you added.
setTimeout(() => {
x.textContent += " content";
}, 500);
span { background: blue }
<div id="x"><span>Example</span></div>
Instead, create a text node and append it like you append the span.
Aside: You only create one span and then repeatedly modify and move it. You probably want to create multiple span elements instead.
The reason why the tags are not visible in the DOM is because you are using the same instance of the element in the entire loop. Instead, you should create a new instance of the element inside the loop so that a new tag is created for each iteration.
Here's the updated code:
const holder = document.querySelector('.holder');
const body = document.querySelector('body');
const html = document.querySelector('html');
html.style.backgroundColor = 'gray';
for(let i = 1; i <= 100; i++) {
const span = document.createElement('span');
if (i % 3 === 0 && i % 5 === 0) {
span.setAttribute('class', 'fizzBuzz');
span.style.color = 'brown';
span.textContent = 'FizzBuzz, ';
holder.appendChild(span);
} else if (i % 5 === 0) {
span.setAttribute('class', 'buzz');
span.textContent = 'Buzz, ';
holder.appendChild(span);
} else if (i % 3 === 0) {
span.setAttribute('class', 'fizz');
span.textContent = 'Fizz, ';
holder.appendChild(span);
} else {
holder.textContent += `${i}, `;
holder.style.color = 'white';
}
if (i === 100) {
holder.textContent = holder.textContent.slice(holder.textContent, holder.textContent.length - 2);
holder.textContent += '.';
}
}
body.appendChild(holder);
This question already has an answer here:
Basic questions about javascript
(1 answer)
Closed 5 years ago.
I am working on a script that would generate random list of 100 elements where every third element would be clickable. So far I am stuck at stage below. Any ideas how to progress?
var hundred = Array(100);
hundred.toString();
for (i = 0; i < hundred.length; i++) {
document.write("Item " + (i + 1) + " of" + hundred.length + "</br>")
}
I used buttons. every third element will be clickable. remaining elements will have disabled property
var hundred = Array(100);
hundred.toString();
for (i = 0; i < hundred.length; i++) {
if(i%3===0 && i!==0){
var button = document.createElement("button");
button.innerHTML ="Click "+i ;
document.getElementsByTagName('body')[0].appendChild(button);
}else{
var button = document.createElement("button");
button.innerHTML ="Click "+i ;
button.disabled = true;
document.getElementsByTagName('body')[0].appendChild(button);
}
}
Edited: full example
var hundred = Array(100);
var node;
hundred.toString();
for (i = 0; i < hundred.length; i++) {
if(i%3===0 && i!==0){
node = document.createElement("button");
node.addEventListener('click', function() { alert('clicked'); });
node.innerHTML = 'clickablke';
} else {
node = document.createElement("div");
node.innerHTML = 'just div';
}
document.body.appendChild(node);
}
First you need create the element. Then apply the onclick with this consition i%3 == 0 to every 3 rd element
Updated
after click its a bolder using classList.add()
for (i = 1; i < 10; i++) {
var s = document.createElement('SPAN');
if (i % 3 == 0) {
s.className = 'clickable';
s.onclick = clicks;
}
s.textContent=i;
document.body.appendChild(s)
}
function clicks() {
console.log(this.innerHTML)
this.classList.add('bold')
}
.clickable {
color: red;
}
.bold{
font-weight:bolder;
}
As commented,
Instead of using document.write, use document.createElement to create an element and assign them event listener and append these elements to an element in html or document.body
var hundred = Array(100);
for (i = 0; i < hundred.length; i++) {
let el = document.createElement('span');
el.textContent = i + " ";
if((i+1) % 3 === 0){
el.classList.add('clickable')
el.addEventListener("click", notify)
}
document.body.appendChild(el)
}
function notify(){
this.classList.add('clicked')
console.log(this.textContent)
}
.clickable{
color: blue;
text-decoration: underline;
}
.clicked{
color: gray;
}
References
Why is document.write considered a "bad practice"?
Document.createElement
add onclick event to newly added element in javascript
Multiple wayst to do this, i'd make an event listener to every item reference, hence: every third clickable element goes bold:
var hundred = Array(100);
hundred.toString();
var btn = Array(100);
for (i = 0; i < hundred.length+1; i++) {
btn = document.createElement("p");
btn.innerHTML="Item " + (i-1 + 1) + " of" + hundred.length + "</br>";
if(i%3===0 && i!==0){
btn.addEventListener('click', function() {
this.style.fontWeight = 'bold'; }, false);
}
document.body.appendChild(btn);
}
Interesting issue arising for me.
I am working on an infinite scroll thing in javascript that requires getting clientHeight of a div at some point:
here is the code:
document.addEventListener('DOMContentLoaded', init, false);
function init(){
var j = 0;
var inner1 = document.getElementById("innerDiv1");
inner1.addEventListener("scroll", function(event){ window.j = 2; checkForLoad();});
var inner2 = document.getElementById("innerDiv2");
inner2.addEventListener("scroll", function(event){ window.j = 1; checkForLoad();});
}
var i = 0;
var checkForLoad = function() {
var bottomDiv;
var bottomDivChild;
var offsetOfDiv;
var offsetOfPage;
if(window.j === 2){
bottomDiv = document.getElementById("innerDiv1");
bottomDivChild = bottomDiv.lastElementChild;
offsetOfDiv = bottomDivChild.offsetTop + bottomDivChild.clientHeight; //WORKS FINE
offsetOfPage = (document.getElementById("innerDiv1").pageYOffset) + (document.getElementById("innerDiv1").clientHeight); //THIS IS WHERE THE ISSUE IS
}
else if(window.j === 1){
bottomDiv = document.querySelector("innerDiv2 > div:last-child");
offsetOfDiv = bottomDiv.offsetTop + bottomDiv.clientHeight;
offsetOfPage = inner1.pageYOffset + inner1.innerHeight;
}
if(offsetOfPage > offsetOfDiv - 10) {
alert("time to add");
//eventually will be AJAX with XSL
if (i % 5 !== 0) {
i++;
alert("into the append part");
var newResults = document.createElement("div");
newDiv.innerHtml = "testing " + i;
if(window.j === 2){
document.getElementById("innerDiv1").appendChild(newResults);
}
else if(window.j === 1){
document.getElementById("innerDiv2").appendChild(newResults);
}
checkForLoad();
} else if (i%5 === 0) {
newDiv = document.createElement("div");
newDiv.innerHTML = "Show more Results! " + i;
document.getElementById("scrollingDiv").appendChild(newDiv);
newDiv.setAttribute("ID", "showResults");
newDiv.setAttribute("onClick", "showMore()");
}
}
};
I realize that there are some inconsistencies here, but they are in parts of the script i am not using. Here is the issue though, in the if(window.j === 2) statement, you can see that I use client height in a couple places. In the first one, it returns a number perfectly and life goes on as planned, but when I use it in the line where I get the "offSerOfPage", the sum becomes NaN. I am using firebug to debug it and when I add document.getElementById("innerDiv1").clientHeight to the watch list, it shows that it has a number attatched to it. Yet the sum is returned as NaN. I think my wording is a bit confusing here so if you need clarification, please ask! Thanks!
This question already has answers here:
What do querySelectorAll and getElementsBy* methods return?
(12 answers)
Closed 8 years ago.
I have this code which is working. But it is only for one textarea because it uses ID. However i would like to use this for all textareas that on my code. I have tried getElementsByClassName but it didn't work.
HTML:
<textarea id="textarea1" class="form-control page-textarea" rows="4" style="height:92px;" name="memory" placeholder="new comment..."></textarea>
JS:
<script type="text/javascript">
var textarea = document.getElementById("textarea1");
var limit = 200;
textarea.oninput = function() {
textarea.style.height = "";
textarea.style.height = Math.min(textarea.scrollHeight, limit) + "px";
if(textarea.style.height == "92px"){
textarea.style.overflow = "hidden";
} else if(textarea.style.height < limit + "px"){
textarea.style.overflow = "hidden";
} else {
textarea.style.overflow = "auto";
}
};
</script>
How do i do this?
If you take a look at the docs you'll see that getElementsByClassName returns a HTMLCollection. You will have to loop through all the child elements to make it work.
var textareas = document.getElementsByClassName("form-control page-textarea");
var limit = 200;
for(var i=0, length= textareas.length; i < length; i++){
var textarea = textareas[i];
textarea.oninput = function() {
this.style.height = "";
this.style.height = Math.min(this.scrollHeight, limit) + "px";
if(this.style.height == "92px"){
this.style.overflow = "hidden";
} else if(this.style.height < limit + "px"){
this.style.overflow = "hidden";
} else {
this.style.overflow = "auto";
}
};
}
FIDDLE
I thing event delegation is much better approach in this situation.
Check this code.
document.addEventListener('input', function(event) {
var limit = 200;
if(event.target.getAttribute("class") == "form-control page-textarea") {
var target = event.target;
target.style.height = "";
target.style.height = Math.min(target.scrollHeight, limit) + "px";
if(target.style.height == "92px"){
target.style.overflow = "hidden";
} else if(target.style.height < limit + "px"){
target.style.overflow = "hidden";
} else {
target.style.overflow = "auto";
}
}
}, false);
Here is working jsFiddle.
http://jsfiddle.net/yg6sS/5/
In this way we save loop trought elements.
use jquery to easily solve this
$(function() {
$("textarea").css("border", "3px solid red");
(OR)
$("textarea").css('border':'3px solid red','color':'Green');
});
I'm attempting to make a tree view using JavaScript. I have a list of Divs created dynamically. A parent node that is showing and a child node that will not be showing. When you hit the button on the parent node it will call show('slow'). This works on IE8 and IE9, but when I test it with IE7, the child node will show with out it's CSS class. How can I make this work with IE7?
code in the main page
function CreateEventCategoryDiv(EventCategoryName) {
var NewEventCategoryNode = document.createElement('div');
NewEventCategoryNode.id = EventCategoryName + "Node";
if (TreeNodeCounter == 0 || (TreeNodeCounter % 2) == 0) {
NewEventCategoryNode.className = "EventCategoryNodesEven";
}
else {
NewEventCategoryNode.className = "EventCategoryNodesOdd";
}
NewEventCategoryNode.innerHTML = "<input type='button' value='+' id='ExpandButton' class='ExpandNodeButtons' onclick='ExpandNode(\"" + EventCategoryName + "\");' /> " + EventCategoryName;
var EventTree = document.getElementById("EventTree");
EventTree.appendChild(NewEventCategoryNode);
TreeNodeCounter++;
}
function ExpandNode(PassedNode) {
var ParentNode = CalendarObject.SearchCategoryNode(PassedNode);
if (ParentNode.IsChildrenShowing == false) {
ParentNode.ExpandNodes(CalendarObject.Months);
}
else if (ParentNode.IsChildrenShowing == true) {
ParentNode.CollapseNode(CalendarObject.Months);
}
}
This Part is called in the EventCategory Class to add the child nodes(sorry I forgot this one at first)
this.AddEventType = function (EventTypeNode) {
var NewElement = document.createElement('Div');
NewElement.id = EventTypeNode.DivAssociateId;
NewElement.innerText = EventTypeNode.Name;
if (this.NodesCount == 0 || (this.NodesCount % 2) == 0) {
NewElement.setAttribute("class", "EventTypeNodesEven");
}
else {
NewElement.setAttribute("class", "EventTypeNodesOdd");
}
NodesCount = this.EventTypeNodesArray.push(EventTypeNode);
$(NewElement).hide();
var ParentElement = document.getElementById("EventTree");
ParentElement.appendChild(NewElement);
this.NodesCount++;
};
This Part is in the CalendarGrid class
this.ExpandNodes = function (MonthArray) {
for (var x in this.EventTypeNodesArray) {
var SelectedNode = document.getElementById(this.EventTypeNodesArray[x].DivAssociateId);
if (this.IsChildrenShowing == false) {
$(SelectedNode).show('slow');
for (var y = 0; y < MonthArray.length; y++) {
var SelectedRow = document.getElementById(this.EventTypeNodesArray[x].Name + MonthArray[y].MonthName + "Row");
$(SelectedRow).show('slow');
}
}
}
this.IsChildrenShowing = true;
};
CSS Code:
.EventTypeNodesOdd
{
font-family:Arial;
font-weight:bold;
text-align:center;
height:27px;
background-color:#dbe2e6;
display:block;
}
.EventTypeNodesEven
{
font-family:Arial;
font-weight:bold;
text-align:center;
height:27px;
background-color:#f9fafb;
}
Try setting the class to whatever it should be after showing it.