<form class="m-5 box" action="javascript:void(0);">
	<div class="field">
		<label class="label">Minimum</label>
		<div class="control">
			<input class="input" type="number" placeholder="ex. 0" id="fizzbuzz-min" value="0">
		</div>
	</div>
	<div class="field">
		<label class="label">Maximum</label>
		<div class="control">
			<input class="input" type="number" placeholder="ex. 100" id="fizzbuzz-max" value="100">
		</div>
	</div>
	<div class="field">
		<label class="checkbox">
			<input type="checkbox" id="fizzbuzz-reverse">
			<s>I agree to the Terms and Conditions wait what</s> Reverse order
		</label>
	</div>
	<div class="field">
		<div class="control">
			<button class="button is-primary" id="fizzbuzz-load">Load</button>
		</div>
	</div>
</form>

<button class="button is-primary m-5" id="fizzbuzz-increase">Increase</button>
<button class="button is-primary mt-5 mr-5" id="fizzbuzz-decrease">Decrease</button>
<button class="button is-link mt-5 mr-5" id="fizzbuzz-complete">Complete the run</button>
<button class="button is-danger mt-5" id="fizzbuzz-reset">Reset</button>
<button class="button is-danger mt-5 ml-5" id="fizzbuzz-clear">Clear logs</button>

<div id="fizzbuzz-logs" class="container m-5 box"></div>
function FizzBuzz(options = {}) {
	options = Object.assign(options || {}, {
		wordmap: [{ word: "Fizz", divisor: 3 }, { word: "Buzz", divisor: 5 }],
		counter: 0,
		minimum: 0,
		maximum: 100,
		reverse: false,
		increaseElement: document.getElementById("fizzbuzz-increase"),
		decreaseElement: document.getElementById("fizzbuzz-decrease"),
		completeElement: document.getElementById("fizzbuzz-complete"),
		resetElement: document.getElementById("fizzbuzz-reset"),
		clearElement: document.getElementById("fizzbuzz-clear"),
		logsElement: document.getElementById("fizzbuzz-logs"),
		formMinElement: document.getElementById("fizzbuzz-min"),
		formMaxElement: document.getElementById("fizzbuzz-max"),
		formReverseElement: document.getElementById("fizzbuzz-reverse"),
		formLoadButton: document.getElementById("fizzbuzz-load"),
	});
	
	const getCounter = () => +localStorage.getItem("fizzbuzz");
	const setCounter = number => localStorage.setItem("fizzbuzz", number.toString());
	const clearCounter = () => localStorage.removeItem("fizzbuzz");
	const outputResult = data => options.logsElement.innerHTML += data + "<br>";

	const getCurrentPosInfo = () =>
			`<strong>Direction:</strong> ${
				options.reverse ? "backwards" : "forwards"
			}; <strong>Previous</strong>: ${
				fizzBuzzIndividual(options.counter - 1)
			}; <strong>Current</strong>: ${
				fizzBuzzIndividual(options.counter)
			}; <strong>Next</strong>: ${
				fizzBuzzIndividual(options.counter + 1)
			};`;

	if (getCounter()) options.counter = getCounter();

	function toggleButtons() {
		if (options.counter >= options.maximum) {
			options.increaseElement.setAttribute("disabled", "true");
			options.decreaseElement.removeAttribute("disabled");
		} else {
			options.increaseElement.removeAttribute("disabled");
		}

		if (options.counter <= options.minimum) {
			options.decreaseElement.setAttribute("disabled", "true");
			options.increaseElement.removeAttribute("disabled");
		} else {
			options.decreaseElement.removeAttribute("disabled");
		}
	}

	function fizzBuzzIndividual(number) {
		if (number === 0) return "0";
		else {
			const matches = options.wordmap
				.filter(word => remainder(number, word.divisor) === 0)
				.map(word => word.word);

			return matches.length > 0 ? matches.join("") : number;
		}
	}

	function recurseFizzBuzz(
		minimum = 0,
		point = 0,
		maximum = 100,
		reverseOrder = false
	) {
		let fizzBuzzed = [];

		const inner = (min, start, max) => {
			const fizzBuzzed = [];

			if (start !== (reverseOrder ? min : max)) {
				fizzBuzzed.push(fizzBuzzIndividual(start, options.wordmap));
				fizzBuzzed.push(...inner(min, start + (reverseOrder ? -1 : 1), max));
			} else fizzBuzzed.push("Done!");

			return fizzBuzzed;
		};

		fizzBuzzed = inner(minimum, point, maximum);

		options.counter = maximum;
		toggleButtons();

		return fizzBuzzed;
	}

	function remainder(number1, number2) {
		const quotient = Math.floor(number1 / number2);
		const product = quotient * number2;
		return number1 - product;
	}

	options.increaseElement.addEventListener("click", () => {
		if (options.counter < options.maximum) options.counter++;

		toggleButtons();
		setCounter(options.counter);
		outputResult(getCurrentPosInfo());
	});

	options.decreaseElement.addEventListener("click", () => {
		if (options.counter > options.minimum) options.counter--;

		toggleButtons();
		setCounter(options.counter);
		outputResult(getCurrentPosInfo());
	});

	options.completeElement.addEventListener("click", () => {
		options.reverse ? options.counter-- : options.counter++;
		setCounter(options.counter);
		recurseFizzBuzz(
			options.minimum,
			options.counter,
			options.maximum,
			options.reverse
		).forEach(outputResult);
	});

	options.resetElement.addEventListener("click", () => {
		options.counter = options.minimum;
		clearCounter();
		toggleButtons();
	});

	options.clearElement.addEventListener("click", () => (options.logsElement.innerHTML = ""));

	options.formLoadButton.addEventListener("click", () => {
		const min = +options.formMinElement.value;
		const max = +options.formMaxElement.value;

		if (min >= max) {
			outputResult("<strong><span style='color:red'>Error</span>: the start"
				+ " number must be smaller than the end number.</strong>");
			return;
		}

		options.minimum = +options.formMinElement.value;
		options.maximum = +options.formMaxElement.value;
		options.counter = options.reverse ? options.maximum : options.minimum;
		options.reverse = options.formReverseElement.checked;

		toggleButtons();
	});

	toggleButtons();
}

FizzBuzz();

External CSS

  1. https://cdnjs.cloudflare.com/ajax/libs/bulma/0.9.3/css/bulma.min.css

External JavaScript

This Pen doesn't use any external JavaScript resources.