A Simple Flashcard System in JavaScript

The JavaScript program below, when run, prompts for a lesson file, and, when one is provided, quizzes the user with the questions from it. When the user answers all questions correctly three times in a row, the lesson is counted as complete.

The lesson file should be a text file. Each line in the file represents a question and its correct response, delimited by a semicolon.

To see the code in action, copy it into an .html file and open that file in a web browser that runs JavaScript. Or, for an online version, visit https://thiscouldbebetter.neocities.org/flashcards.html.

UPDATE 2017/01/13 – The program has been modified to allow the user to change the number of times each question must be answered correctly, as well as to reverse the question and answer. Also, some very basic input validation has been added.

flashcards


<html>
<body>

	<!-- user interface -->
	<div style="border:1px solid">
		<label>Lesson File:</label>
		<input id="inputFileLesson" type="file"></input>
		
		<div>
			<label>Reverse Question and Answer:</label>
			<input id="checkboxReverse" type="checkbox"></input>
		</div>

		<div>
			<label>Times Each Question Must Be Answered Correctly:</label>		
			<input id="inputTimesCorrectPerQuestion" type="number" value="3"></input>
		</div>

		<button id="buttonStart" onclick="buttonStart_Clicked();">Start Lesson</button>
	</div>

	<div style="border:1px solid">
		<div>
			<div><label>Question:</label></div>
			<textarea id="textareaPresentation" readonly="readonly"></textarea>
		</div>

		<div>
			<div><label>Answer:</label></div>
			<input id="inputResponse"></input>
		</div>

		<p id="pStatusMessage">Upload a valid lesson file and click the Start button to begin.</p>
	</div>
	
<script type="text/javascript">

// ui event handlers

function buttonStart_Clicked()
{
	var inputFileLesson = document.getElementById("inputFileLesson");
	var file = inputFileLesson.files[0];
	
	if (file == null)
	{
		alert("A valid lesson file must be specified by clicking the Lesson File button.");
	}
	else
	{
		var fileReader = new FileReader();
		fileReader.onload = inputFileLesson_Changed_FileLoaded;
		fileReader.readAsText(file);
	}
}

function inputFileLesson_Changed_FileLoaded(event)
{
	var fileContents = event.target.result;

	var questionsAsStrings = fileContents.split("\n");

	var questions = [];

	for (var i = 0; i < questionsAsStrings.length; i++)
	{
		var questionAsString = questionsAsStrings[i];

		var presentationAndResponse = questionAsString.split(";");

		var presentation = presentationAndResponse[0];
		var response = presentationAndResponse[1].trim();

		var question = new Question(presentation, response);

		questions.push(question);
	}
	
	var checkboxReverse = document.getElementById("checkboxReverse");
	var arePresentationAndResponseReversed = checkboxReverse.checked;
	
	var inputTimesCorrectPerQuestion = document.getElementById
	(
		"inputTimesCorrectPerQuestion"
	);
	var timesCorrectPerQuestion = parseInt(inputTimesCorrectPerQuestion.value);
	if (isNaN(timesCorrectPerQuestion) == true || timesCorrectPerQuestion <= 0)
	{
		alert("A positive number must be entered in the Times Each Question Must Be Answered Correctly box.");
	}
	else
	{
		var lessonDefn = new LessonDefn
		(
			questions, 
			arePresentationAndResponseReversed,
			timesCorrectPerQuestion
		);

		Globals.Instance.initialize(lessonDefn);
	}
}

// classes

function DisplayHelper()
{
	// do nothing
}
{
	DisplayHelper.prototype.displayLessonRun = function(lessonRun)
	{
		var textareaPresentation = document.getElementById
		(
			"textareaPresentation"
		);

		var questionCurrent = lessonRun.questionCurrent();
		var presentation = 
		(
			lessonRun.defn.arePresentationAndResponseReversed == true 
			? questionCurrent.responseCorrect 
			: questionCurrent.presentation
		);
		textareaPresentation.value = presentation;

		var inputResponse = document.getElementById("inputResponse");
		inputResponse.value = "";
		inputResponse.focus();

		var pStatusMessage = document.getElementById("pStatusMessage");
		pStatusMessage.innerHTML = lessonRun.statusMessage;
	}
}

function Globals()
{
	// do nothing
}
{
	// instance

	Globals.Instance = new Globals();

	// methods

	Globals.prototype.initialize = function(lessonDefn)
	{
		this.lessonDefn = lessonDefn;

		this.lessonRun = new LessonRun(this.lessonDefn);

		this.displayHelper = new DisplayHelper();
		this.inputHelper = new InputHelper();

		this.lessonRun.initialize();

		this.inputHelper.initialize();

		this.displayHelper.displayLessonRun(this.lessonRun);
	}
}


function InputHelper()
{
	// do nothing
}
{
	InputHelper.prototype.initialize = function()
	{
		document.body.onkeydown = this.handleEventKeydown.bind(this);
	}

	// event handlers

	InputHelper.prototype.handleEventKeydown = function(event)
	{
		if (event.key == "Enter")
		{
			var inputResponse = document.getElementById("inputResponse");
			var responseActual = inputResponse.value.trim();
			
			var lessonRun = Globals.Instance.lessonRun;
			var questionCurrent = lessonRun.questionCurrent();
			var responseExpected = 
			(
				lessonRun.defn.arePresentationAndResponseReversed == true 
				? questionCurrent.presentation 
				: questionCurrent.responseCorrect
			);
	
			var responseRecordCurrent = lessonRun.responseRecordCurrent();

			if (responseActual == responseExpected)
			{
				responseRecordCurrent.timesCorrect++;
				lessonRun.statusMessage = 
					"Correct!  The previous question has been answered correctly "
					+ responseRecordCurrent.timesCorrect
					+ " times in a row.";
			}
			else
			{
				responseRecordCurrent.timesCorrect = 0;
				lessonRun.statusMessage = 
					"Incorrect!  The correct answer was "
					+ responseExpected
					+ ".  You answered "
					+ responseActual
					+ ".";	
			}

			if (lessonRun.isComplete() == true)
			{
				lessonRun.statusMessage = 
					"Lesson complete!  Each question was answered correctly " 
					+ lessonRun.defn.timesCorrectPerQuestion
					+ " times in a row.";
				document.body.onkeydown = null;
			}
			else
			{
				lessonRun.questionAdvance();
			}

			Globals.Instance.displayHelper.displayLessonRun
			(
				lessonRun
			);
		}
		
	}
}

function LessonDefn(questions, arePresentationAndResponseReversed, timesCorrectPerQuestion)
{
	this.questions = questions;
	this.arePresentationAndResponseReversed = arePresentationAndResponseReversed;
	this.timesCorrectPerQuestion = timesCorrectPerQuestion;
}

function LessonRun(defn)
{
	this.defn = defn;
	this.statusMessage = 
		"Each question must be correctly answered " 
		+ this.defn.timesCorrectPerQuestion
		+ " times in a row.";
}
{
	LessonRun.prototype.initialize = function()
	{
		this.questionIndexCurrent = 0;
		this.responseRecords = [];

		var questions = this.defn.questions;

		for (var i = 0; i < questions.length; i++)
		{
			var question = questions[i];
			var responseRecord = new ResponseRecord();
			this.responseRecords.push(responseRecord);
		}
	}

	LessonRun.prototype.isComplete = function()
	{
		var returnValue = true;

		var timesRequired = this.defn.timesCorrectPerQuestion;

		for (var i = 0; i < this.responseRecords.length; i++)
		{
			var responseRecord = this.responseRecords[i];
			if (responseRecord.timesCorrect < timesRequired)
			{
				returnValue = false;
				break;
			}
		}	

		return returnValue;
	}

	LessonRun.prototype.questionAdvance = function()
	{
		var isFirstTime = true;
		var timesRequired = this.defn.timesCorrectPerQuestion;

		while 
		(
			isFirstTime == true 
			|| this.responseRecordCurrent().timesCorrect >= timesRequired
		)
		{
			isFirstTime = false;

			this.questionIndexCurrent++;

			if (this.questionIndexCurrent >= this.defn.questions.length)
			{
				this.questionIndexCurrent = 0;
			}
		}
	}

	LessonRun.prototype.questionCurrent = function()
	{
		return this.defn.questions[this.questionIndexCurrent];
	}

	LessonRun.prototype.responseRecordCurrent = function()
	{
		return this.responseRecords[this.questionIndexCurrent];
	}

}

function Question(presentation, responseCorrect)
{
	this.presentation = presentation;
	this.responseCorrect = responseCorrect;
}

function ResponseRecord()
{
	this.timesCorrect = 0;
}

</script>

</body>
</html>

Advertisements
This entry was posted in Uncategorized and tagged , , , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s