Null Error

416 views
Skip to first unread message

Isaac Franks

unread,
Dec 20, 2018, 12:16:02 PM12/20/18
to DroidScript
I had this null error again. I tried setting up the code that BareK showed me so that I can study it and learn from it, but before I can study it I have to fix it because apparently it wasn't written correctly on the article https://www.sitepoint.com/simple-javascript-quiz/. How do I solve the null issue? I can't study the code until its fixed.
Survey Logic.spk

BareK

unread,
Dec 20, 2018, 12:51:02 PM12/20/18
to DroidScript
This is because your javascript is executed before the DOM has been loaded, that's why it returns 'null' instead of your DOM Element.
One way (among others) to make it work is to perform these actions into your OnStart function:

const quizContainer = document.getElementById("quiz");
const resultsContainer = document.getElementById("results");
const submitButton = document.getElementById("submit");
       
// display quiz right away
buildQuiz
();
       
const previousButton = document.getElementById("previous");
const nextButton = document.getElementById("next");
const slides = document.querySelectorAll(".slide");
let currentSlide
= 0;
       
showSlide
(0);
       
// on submit, show results
submitButton
.addEventListener("click", showResults);
previousButton
.addEventListener("click", showPreviousSlide);
nextButton
.addEventListener("click", showNextSlide);

Isaac Franks

unread,
Dec 22, 2018, 3:33:32 PM12/22/18
to DroidScript
What do I do if there is no OnStart because it is an html app?

BareK

unread,
Dec 22, 2018, 5:04:28 PM12/22/18
to DroidScript
You have this line in your app:
<body onload="app.Start()">
Meaning that when your body HTML is loaded, it will call app.Start().

This will (in short) execute your OnStart function if its present (so you can add it if you want).

If you prefer to go "HTML only", just specify the function you want to be executed.

For exemple:
<body onload="myFunc()">

Isaac Franks

unread,
Dec 26, 2018, 10:30:50 AM12/26/18
to DroidScript
I feel like an idiot, but I have been trying to figure out what you said, been reviewing and rewriting the code to make sense of it. Where should the OnStart function go? Shouldn't it be placed in the script section? I have attached a sample of my code. Did I just make more redundancies?

<html>
<head>
    <meta name="viewport" content="width=device-width">
    <script src='file:///android_asset/app.js'></script>
</head>
   
<script>

function OnStart ()


const quizContainer = document.getElementById("quiz");
const resultsContainer = document.getElementById("results");
const submitButton = document.getElementById("submit");
      
// display quiz right away
buildQuiz();
      
const previousButton = document.getElementById("previous");
const nextButton = document.getElementById("next");
const slides = document.querySelectorAll(".slide");
let currentSlide = 0;
      
showSlide(0);
      
// on submit, show results
submitButton.addEventListener("click", showResults);
previousButton.addEventListener("click", showPreviousSlide);
nextButton.addEventListener("click", showNextSlide);
  
   (function() {
  const myQuestions = [
    {
      question: "Who is the strongest?",
      answers: {
        a: "Superman",
        b: "The Terminator",
        c: "Waluigi, obviously"
      },
      correctAnswer: "c"
    },
    {
      question: "What is the best site ever created?",
      answers: {
        a: "SitePoint",
        b: "Simple Steps Code",
        c: "Trick question; they're both the best"
      },
      correctAnswer: "c"
    },
    {
      question: "Where is Waldo really?",
      answers: {
        a: "Antarctica",
        b: "Exploring the Pacific Ocean",
        c: "Sitting in a tree",
        d: "Minding his own business, so stop asking"
      },
      correctAnswer: "d"
    }
  ];

  function buildQuiz() {
    // we'll need a place to store the HTML output
    const output = [];

    // for each question...
    myQuestions.forEach((currentQuestion, questionNumber) => {
      // we'll want to store the list of answer choices
      const answers = [];

      // and for each available answer...
      for (letter in currentQuestion.answers) {
        // ...add an HTML radio button
        answers.push(
          `<label>
             <input type="radio" name="question${questionNumber}" value="${letter}">
              ${letter} :
              ${currentQuestion.answers[letter]}
           </label>`
        );
      }

      // add this question and its answers to the output
      output.push(
        `<div class="slide">
           <div class="question"> ${currentQuestion.question} </div>
           <div class="answers"> ${answers.join("")} </div>
         </div>`
      );
    });

    // finally combine our output list into one string of HTML and put it on the page
    quizContainer.innerHTML = output.join("");
  }



  function showResults() {
    // gather answer containers from our quiz
    const answerContainers = quizContainer.querySelectorAll(".answers");

    // keep track of user's answers
    let numCorrect = 0;

    // for each question...
    myQuestions.forEach((currentQuestion, questionNumber) => {
      // find selected answer
      const answerContainer = answerContainers[questionNumber];
      const selector = `input[name=question${questionNumber}]:checked`;
      const userAnswer = (answerContainer.querySelector(selector) || {}).value;

      // if answer is correct
      if (userAnswer === currentQuestion.correctAnswer) {
        // add to the number of correct answers
        numCorrect++;

        // color the answers green
        answerContainers[questionNumber].style.color = "lightgreen";
      } else {
        // if answer is wrong or blank
        // color the answers red
        answerContainers[questionNumber].style.color = "red";
      }
    });

    // show number of correct answers out of total
    resultsContainer.innerHTML = `${numCorrect} out of ${myQuestions.length}`;
  }

  function showSlide(n) {
    slides[currentSlide].classList.remove("active-slide");
    slides[n].classList.add("active-slide");
    currentSlide = n;
   
    if (currentSlide === 0) {
      previousButton.style.display = "none";
    } else {
      previousButton.style.display = "inline-block";
    }
   
    if (currentSlide === slides.length - 1) {
      nextButton.style.display = "none";
      submitButton.style.display = "inline-block";
    } else {
      nextButton.style.display = "inline-block";
      submitButton.style.display = "none";
    }
  }

  function showNextSlide() {
    showSlide(currentSlide + 1);
  }

  function showPreviousSlide() {
    showSlide(currentSlide - 1);

  }

 
const quizContainer = document.getElementById("quiz");
const resultsContainer = document.getElementById("results");
const submitButton = document.getElementById("submit");
      
// display quiz right away
buildQuiz();
      
const previousButton = document.getElementById("previous");
const nextButton = document.getElementById("next");
const slides = document.querySelectorAll(".slide");
let currentSlide = 0;
      
showSlide(0);
      
// on submit, show results
submitButton.addEventListener("click", showResults);
previousButton.addEventListener("click", showPreviousSlide);
nextButton.addEventListener("click", showNextSlide);
})();
</script>

<style>
    @import url(https://fonts.googleapis.com/css?family=Work+Sans:300,600);

body{
    font-size: 20px;
    font-family: 'Work Sans', sans-serif;
    color: #333;
  font-weight: 300;
  text-align: center;
  background-color: #f8f6f0;
}
h1{
  font-weight: 300;
  margin: 0px;
  padding: 10px;
  font-size: 20px;
  background-color: #444;
  color: #fff;
}
.question{
  font-size: 30px;
  margin-bottom: 10px;
}
.answers {
  margin-bottom: 20px;
  text-align: left;
  display: inline-block;
}
.answers label{
  display: block;
  margin-bottom: 10px;
}
button{
  font-family: 'Work Sans', sans-serif;
    font-size: 22px;
    background-color: #279;
    color: #fff;
    border: 0px;
    border-radius: 3px;
    padding: 20px;
    cursor: pointer;
    margin-bottom: 20px;
}
button:hover{
    background-color: #38a;
}



.slide{
  position: absolute;
  left: 0px;
  top: 0px;
  width: 100%;
  z-index: 1;
  opacity: 0;
  transition: opacity 0.5s;
}
.active-slide{
  opacity: 1;
  z-index: 2;
}
.quiz-container{
  position: relative;
  height: 200px;
  margin-top: 40px;
}
</style>

<body onload="app.Start()">


<h1>Quiz on Important Facts</h1>
<div class="quiz-container">
  <div id="quiz"></div>
</div>
<button id="previous">Previous Question</button>
<button id="next">Next Question</button>
<button id="submit">Submit Quiz</button>
<div id="results"></div>


   
</body>
</html>

BareK

unread,
Dec 26, 2018, 12:26:11 PM12/26/18
to DroidScript
It's almost it, but you have to enclose your code in brackets:

function OnStart()
{
//your code hère
}

Isaac Franks

unread,
Dec 26, 2018, 1:59:19 PM12/26/18
to DroidScript
Seriously? So, just to confirm, does the code make sense now?

Isaac Franks

unread,
Dec 26, 2018, 2:14:20 PM12/26/18
to DroidScript
I just ran the above code and an error returned saying "buildQuiz is not defined" Line 16

BareK

unread,
Dec 27, 2018, 3:12:03 PM12/27/18
to DroidScript
Firstly you didn't move the code parts in your OnStart function but you duplicated them, which is a bad idea.
However this code is maybe a bit to complex for you, so just moving parts of it won't work instantly without modifying a.

BareK

unread,
Dec 27, 2018, 3:17:59 PM12/27/18
to DroidScript
...few parts before.

(Sorry for the double post, I hitted 'tab+enter' by mistake).

So, to make it easy, just move all your script in your OnStart function like this:

<script>
function OnStart()
{
   
//your whole code here
}
</script>

It should work as expected.

But I'd recommand you some further readings about DOM events, variables scopes etc.
So for your learning process and if you're curious, here are some resources:

Steve Garman

unread,
Dec 27, 2018, 3:25:54 PM12/27/18
to DroidScript
@Barek,

I haven't looked at the code in detail but isn't moving all his code into OnStart going to make his global variables local?

This probably applies to global functions too.

BareK

unread,
Dec 27, 2018, 3:34:01 PM12/27/18
to DroidScript
The whole code is in an IIFE so it will be part of it context.
Tested and it works

Isaac Franks

unread,
Dec 28, 2018, 2:17:00 PM12/28/18
to DroidScript
I removed the redundant code and at least placed the OnStart as you had said, but there is still an error. I shared the spk again to see if you might see what I am missing. Or had you meant to move the entire javascript underneath the OnStart?
Survey Logic 2.spk

BareK

unread,
Dec 29, 2018, 1:06:47 AM12/29/18
to DroidScript
Yes I meant you to place all your javascript inside your OnStart function (based on the code you provided in your original post).
I putted two screens to show you the result I have on my device.

I'd like to say that I could have given you the code directly, but it will be more profitable and rewarding for you to make it work by yourself.
You seem to be persevering and patient, this is a good attitude to learn :)

As I said this project is maybe a bit complex to begin with, and for the future I would recommand you some more beginner-friendly material:

Did you take a look at the official DroidScript tutorials?

Or Alex Symbroson's youtube channel?

You can also find collections of relatively good JavaScript material on the web:

And I strongly recommand you to read this wise post from Yaphi Berhanu on sitepoint:

Good luck :)
screen1.png
screen2.png

Isaac Franks

unread,
Dec 29, 2018, 12:14:59 PM12/29/18
to DroidScript
I actually appreciate you not showing me a sample of code because it did allow me to figure it out on my own. I won't be giving up on coding until I understand it so I will definitely be reading through every one of those articles you sent me and even making the Whack-A-Android and SpaceRock games to learn more. This one has been frustrating me for about a month so thanks so much for the help! Happy New Year!

BareK

unread,
Dec 29, 2018, 12:48:20 PM12/29/18
to DroidScript
Your wellcome :)
Keep it up, and happy new year too!
Reply all
Reply to author
Forward
0 new messages