-4

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 the 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 the getElementByID is null.

Makyen
  • 27,758
  • 11
  • 68
  • 106
IsidIoan
  • 333
  • 2
  • 3
  • 11
  • wrap this code or call showOnClick in window.onload...it should work – Geeky Nov 12 '16 at 02:02
  • 2
    Why do you use equal statement == instead of = inside if statements body? I suppose you want to set style.display not compare. Do you load JS script after HTML elements you trying access in JS? – michalk93 Nov 12 '16 at 02:05
  • 1
    yes you are right.thanks for noticing? – IsidIoan Nov 12 '16 at 02:07

3 Answers3

4

Getting null from getElementById() for elements in your HTML

Your specific problem of getting null from your calls to getElementById() is probably caused by your JavaScript running prior to the HTML of your page being fully loaded (i.e. the elements don't exist in the DOM yet, thus null). However, while that is likely the problem, the we can not know that is the 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 the JavaScript is loaded/run in the page).

The solution to the problem of JavaScript running prior to the elements in the page being available is to delay the execution of your JavaScript until the page has loaded. There are multiple ways to do this. One is to just have your <script> tags at the bottom of your HTML. However, delaying until the page has loaded is usually accomplished by wrapping your code (or just your initialization code) in a function which is then assigned as a listener for one of the variety of events which are triggered at the various stages of the <document> being ready. The most common to use is the <document>'s 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.
});

That event can also be accessed as document.onready. Accessing it this way can cause problems when multiple scripts try to do so, as 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 by using JavaScript to add your event listeners and eliminating the string of if statements by first setting all to be not visible, then set the one you want to be visible. In his answer, it is implied that you have to use jQuery to think about the problem using a different structure. jQuery provides many convenient features. One of its most important feature is cross browser compatibility. However, it also provides a large number of predefined methods which allow short syntax access to commonly used features, which, in most cases, implicitly iterate over all elements which are selected. This all comes at the cost of 85KiB of minimized code. Thus, jQuery is inappropriate if you are only doing a few things.

You can implement the same functionality that gavgrif showed in his 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 a bit more general purpose/reusable:

In general, I would prefer to have generic show() and hide() functions, as they might be re-used elsewhere. In addition, the following makes asArray() more robust by handing multiple types of input (most of which is 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 (e.g. 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') {
        //e.g. 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 of code, you could do something like the following [Note: Using ES6 syntax could 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 above code snippets use the HTML provided in gavgrif's answer.

Community
  • 1
  • 1
Makyen
  • 27,758
  • 11
  • 68
  • 106
2

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 corrected function

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 == stands for comparing LHs to RHS and = in display='block' works as assignment operator, assigning value on he right to the object on the left

mrid
  • 5,408
  • 5
  • 19
  • 56
  • 1
    Yes thank you the problem with my function was that accidentally used double == to assign the display values to the divs – IsidIoan Nov 12 '16 at 13:07
1

Though you did not specify jQuery for this question - there is a VERY simple jquery approach. Have the three buttons each with a value that corresponds to the div's. Then in the onclick event (and note the separation of the javascript from the html by removing the inline click handler - better code structure) - you simply get the value of the clicked button, hide() all the divs (using a common class) and then show() the desired one using its id. This could also be done with adding / removing a class of hidden (eg .hidden{display:none}, and it could be done with vanilla javascript - but all those if's.... Note that I added some text to div2 and 3 so that they can be seen to toggle on their respective button clicks.

Note also I am not suggesting to load the entire jQuery library for this one function - too much weight for this small function, but just offering an opnion on allowing a small function to be a part of the larger picture of 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>
gavgrif
  • 13,077
  • 2
  • 17
  • 22
  • 2
    While jQuery has many convenient features, you can make code which is functionally identical to what you have provided with stock JavaScript, just using more characters. A quick, non-optimized, line-by-line equivalent JavaScript implementation takes ~338 characters vs 149 for this jQuery solution (jQuery saving 189 characters). Moderate size optimization can reduce the JavaScript version to 307 characters, 111 more than the equivalent jQuery (ES6 saves another 50, or so, off the JavaScript). Not having jQuery available does not mean you should not think about the problem similarly. – Makyen Nov 12 '16 at 07:59
  • Yes thanks for the suggestion, I am using javascript and unfortunately didn't mention it – IsidIoan Nov 12 '16 at 13:06