Document.getElementById returns null
Hello, I have these divs:
<div id="layoutGroup1"> <h2>UK Map</h2> <div div style="width: 650px; height: 700px;" id="MapDIV"></div> <div id="userUpdateDIV"></div> <div id="BarChartDIV"></div> <div id="divPack1"></div> </div> <div id="layoutGroup2"> <div id="tree"></div> </div> <div id="layoutGroup3"> <div id="map"></div> </div> I want by having three buttons on the screen and clicking to hide two divs and display only one.
button id="userButton" onclick ="showOnClick('layoutGroup1');">ECA </button function showOnClick(element) { if (element == 'layoutGroup1') { document.getElementById('layoutGroup1').style.display == 'block'; document.getElementById('layoutGroup2').style.display == 'none'; document.getElementById('layoutGroup3').style.display == 'none'; } else if (element == 'layoutGroup2') { document.getElementById("layoutGroup1").style.display == 'none'; document.getElementById("layoutGroup2").style.display == 'block'; document.getElementById('layoutGroup3').style.display == 'none'; } else { document.getElementById("layoutGroup3").style.display == "block"; document.getElementById("layoutGroup1").style.display == "none"; document.getElementById("layoutGroup2").style.display == "none"; } } Above is the function I use, although it gives me an error that getElementByID is null .
Getting null from getElementById() for elements of your HTML
Your specific problem of getting null from your calls to getElementById() is probably due to your JavaScript running before the HTML of your page is fully loaded (i.e. the elements do not yet exist in the DOM, thus null ). However, although this is probably a problem, we cannot know that this is a problem because your question does not show us the relationship between your HTML and your JavaScript (i.e. it does not show how and when JavaScript is loaded / launched into the page )
The solution to the JavaScript problem that runs before the elements appear on an accessible page is to delay the execution of JavaScript until the page loads . There are several ways to do this. One of them is just to have <script> tags at the bottom of your HTML. However, the delay before loading the page is usually done by transferring the code (or only the initialization code) to a function, which is then assigned as a listener for one of the many events that are launched at different stages of the <document> ready. The most common is the <document> DOMContentLoaded event . You can do this with the following code:
//Wait to run your initialization code until the DOM is fully loaded. This is needed // when wanting to access elements that are later in the HTML than the <script>. if(document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', afterLoaded); } else { //The DOMContentLoaded event has already fired. Just run the code. afterLoaded(); } afterLoaded() { //Your initialization code goes here. This is from where your code should start // running if it wants to access elements placed in the DOM by your HTML files. // If you are wanting to access DOM elements inserted by JavaScript, you may need // to delay more, or use a MutationObserver to see when they are inserted. }); This event can also be accessed as document.onready . Accessing it this way can cause problems when several scenarios try to do this, since only one can use this method. Thus, it is much better to use the addEventListener() method to listen for this or any other event.
Other aspects of your code
In his answer, gavgrif makes some good points regarding the structure of your code, including separating your HTML from your JavaScript using JavaScript to add event listeners and removing string from if , first setting everything so that they are not visible, and then setting one which you want to see. His answer implies that you must use jQuery to think about the problem using a different structure. jQuery provides many convenient features. One of its most important features is cross-browser compatibility. However, it also provides a large number of predefined methods that provide short syntactic access to commonly used functions, which in most cases implicitly iterate over all selected elements. All this happens by minimizing the 85 KB code. So jQuery is not suitable if you are doing only a few things.
You can implement the same functionality as gavgrif in your answer using vanilla JavaScript.
document.addEventListener('DOMContentLoaded', function(){ //Wait to add event listeners until the DOM is fully loaded. This is needed // when wanting to access elements that are later in the HTML than the <script>. queryAll('.showDiv').forEach(function(el){ el.addEventListener('click',showOnClick); }); }); function showOnClick(event){ var groupNumber=this.value; queryAll('.layoutGroups').forEach(function(el){ el.style.display='none' }); document.querySelector('#layoutGroup'+groupNumber).style.display='block'; } function queryAll(selector){ return asArray(document.querySelectorAll(selector)) } function asArray(obj){ var newArr = []; newArr.push.apply(newArr, obj); return newArr; } <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <button class='showDiv' value="1">1</button> <button class='showDiv' value="2">2</button> <button class='showDiv' value="3">3</button> <div class="layoutGroups" id="layoutGroup1"> <h2>UK Map</h2> <div div style="width: 650px; height: 700px;"id = "MapDIV"></div> <div id="userUpdateDIV"></div> <div id = "BarChartDIV"></div> <div id="divPack1"></div> </div> <div class="layoutGroups" id="layoutGroup2"> <div id= "tree">Tree</div> </div> <div class="layoutGroups" id="layoutGroup3"> <div id = "map">Map</div> </div> Code that is more versatile / reusable:
In general, I would prefer to have the show() and hide() functions in common, as they can be reused elsewhere. In addition, the following: asArray() more reliable, passing in several input types (most of them are not needed here).
document.addEventListener('DOMContentLoaded', function(){ //Wait to add event listeners until the DOM is fully loaded. This is needed // when wanting to access elements that are later in the HTML than the <script>. queryAll('.showDiv').forEach(function(el) { el.addEventListener('click',showOnClick) }); }); function showOnClick(event){ var groupNumber = this.value; hide(queryAll('.layoutGroups')); show(queryDoc('#layoutGroup'+groupNumber)); } function hide(arraylikeOrElement) { setDisplay(arraylikeOrElement,'none') } function show(arraylikeOrElement) { setDisplay(arraylikeOrElement,'block') } function setDisplay(arraylikeOrElement,text) { setAStyle(arraylikeOrElement,'display',text); } function setAStyle(arraylikeOrElement,which,text) { asArray(arraylikeOrElement).forEach(function(el) { el.style[which]=text; }); } function queryAll(selector){ //Returns all matches in the document return asArray(document.querySelectorAll(selector)); } function queryDoc(selector){ //Returns only the first match in the document (useful for IDs). This is faster // than querySelectorAll because it does not search the entire DOM. It stops // after the first match. return document.querySelector(selector); } function asArray(obj) { //accepts Arrays, array-like Objects (eg NodeLists), single elements, primitives // returns an array, even if the array only has one entry var newArr = []; if(typeof obj !== 'object' || obj instanceof Node) { return [obj]; } if(Array.isArray(obj)){ return obj; } if(obj === null) { return null; } if(typeof obj.length === 'number') { //NodeList and other array-like objects: faster in most browsers and // more compatible than Array.from(). newArr.push.apply(newArr, obj); return newArr; } if(typeof obj.nextNode === 'function') { //eg TreeWalkers, NodeIterator var currentNode; while(currentNode = nodeIter.nextNode()) { newArr.push(currentNode); } return newArr; } if(typeof Array.from === 'function') { return Array.from(obj); } //Could make this much more complex to handle more types of Objects, but not in // this demo code. //Indicate that we don't know what to do with the Object return null; } <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <button class='showDiv' value="1">1</button> <button class='showDiv' value="2">2</button> <button class='showDiv' value="3">3</button> <div class="layoutGroups" id="layoutGroup1"> <h2>UK Map</h2> <div div style="width: 650px; height: 700px;"id = "MapDIV"></div> <div id="userUpdateDIV"></div> <div id = "BarChartDIV"></div> <div id="divPack1"></div> </div> <div class="layoutGroups" id="layoutGroup2"> <div id= "tree">Tree</div> </div> <div class="layoutGroups" id="layoutGroup3"> <div id = "map">Map</div> </div> More compact code:
If you are looking for brevity, you can do something like the following [Note: using ES6 syntax can further reduce the number of characters used.]:
var d=document,q=function(s){return Array.prototype.slice.call(d.querySelectorAll(s))}; d.onready=function(){ //Using document.ready is not a good idea, use addEventListener. q('.showDiv').forEach(function(e){e.addEventListener('click',function(){ var element=this.value; q('.layoutGroups').forEach(function(e){e.style.display='none'}); q('#layoutGroup'+element)[0].style.display='block'; })}) } <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <button class='showDiv' value="1">1</button> <button class='showDiv' value="2">2</button> <button class='showDiv' value="3">3</button> <div class="layoutGroups" id="layoutGroup1"> <h2>UK Map</h2> <div div style="width: 650px; height: 700px;"id = "MapDIV"></div> <div id="userUpdateDIV"></div> <div id = "BarChartDIV"></div> <div id="divPack1"></div> </div> <div class="layoutGroups" id="layoutGroup2"> <div id= "tree">Tree</div> </div> <div class="layoutGroups" id="layoutGroup3"> <div id = "map">Map</div> </div> The code snippets above use the HTML provided by gavgrif's answer .
function showOnClick(element){ if(element=='layoutGroup1'){ document.getElementById('layoutGroup1').style.display='block'; document.getElementById('layoutGroup2').style.display='none'; document.getElementById('layoutGroup3').style.display='none'; } else if(element=='layoutGroup2'){ document.getElementById("layoutGroup1").style.display='none'; document.getElementById("layoutGroup2").style.display='block'; document.getElementById('layoutGroup3').style.display='none'; } else{ document.getElementById("layoutGroup3").style.display="block"; document.getElementById("layoutGroup1").style.display="none"; document.getElementById("layoutGroup2").style.display="none"; } } #layoutGroup1, #layoutGroup2, #layoutGroup3{ display: none; } <button id="userButton1" onclick ="showOnClick('layoutGroup1');">ECA </button> <button id="userButton2" onclick ="showOnClick('layoutGroup2');">button 2 </button> <button id="userButton3" onclick ="showOnClick('layoutGroup3');">button 3 </button> <div id="layoutGroup1"> <h2>UK Map</h2> <div div style="width: 650px; height: 700px;" id="MapDIV"></div> <div id="userUpdateDIV">this is layoutGroup1</div> <div id="BarChartDIV"></div> <div id="divPack1"></div> </div> <div id="layoutGroup2"> <div id="tree">this is layoutGroup2</div> </div> <div id="layoutGroup3"> <div id="map">this is layoutGroup3</div> </div> This is your fixed feature.
function showOnClick(element){ if(element=='layoutGroup1'){ document.getElementById('layoutGroup1').style.display='block'; document.getElementById('layoutGroup2').style.display='none'; document.getElementById('layoutGroup3').style.display='none'; } else if(element=='layoutGroup2'){ document.getElementById("layoutGroup1").style.display='none'; document.getElementById("layoutGroup2").style.display='block'; document.getElementById('layoutGroup3').style.display='none'; } else{ document.getElementById("layoutGroup3").style.display="block"; document.getElementById("layoutGroup1").style.display="none"; document.getElementById("layoutGroup2").style.display="none"; } } In if conditions, == means comparing LH with RHS and = in display='block' acts as an assignment operator, assigning a value to it to the right of the object to the left
Although you did not specify jQuery for this question - there is a very simple jquery approach. Each of the three buttons has a value corresponding to a div. Then in the onclick event (and pay attention to the separation of javascript from html, removing the built-in click handler - the best code structure) - you just get the value of the button clicked, hide () all divs (using a common class) and then show () the desired one using your identifier. This can also be done with the addition / removal of the hidden class (eg .hidden {display: none}, and this can be done with javascript using vanilla), but all these if ... Please note that I added some text to div2 and 3 so that they can see that they switch to the corresponding button presses.
Please note that I also do not suggest loading the entire jQuery library for this one function - too much weight for this small function, but just offers an option that allows a small function to be part of a larger picture of the code structure.
$(document).ready(function(){ $('.showDiv').click(function(){ var element = $(this).val(); $('.layoutGroups').hide(); $('#layoutGroup'+element).show(); }) }) <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <button class='showDiv' value="1">1</button> <button class='showDiv' value="2">2</button> <button class='showDiv' value="3">3</button> <div class="layoutGroups" id="layoutGroup1"> <h2>UK Map</h2> <div div style="width: 650px; height: 700px;"id = "MapDIV"></div> <div id="userUpdateDIV"></div> <div id = "BarChartDIV"></div> <div id="divPack1"></div> </div> <div class="layoutGroups" id="layoutGroup2"> <div id= "tree">Tree</div> </div> <div class="layoutGroups" id="layoutGroup3"> <div id = "map">Map</div> </div>