Pen Settings



CSS Base

Vendor Prefixing

Add External Stylesheets/Pens

Any URL's added here will be added as <link>s in order, and before the CSS in the editor. You can use the CSS from another Pen by using it's URL and the proper URL extention.

+ add another resource


Babel includes JSX processing.

Add External Scripts/Pens

Any URL's added here will be added as <script>s in order, and run before the JavaScript in the editor. You can use the URL of any other Pen and it will include the JavaScript from that Pen.

+ add another resource


Add Packages

Search for and use JavaScript packages from npm here. By selecting a package, an import statement will be added to the top of the JavaScript editor for this package.


Save Automatically?

If active, Pens will autosave every 30 seconds after being saved once.

Auto-Updating Preview

If enabled, the preview panel updates automatically as you code. If disabled, use the "Run" button to update.

Format on Save

If enabled, your code will be formatted when you actively save your Pen. Note: your code becomes un-folded during formatting.

Editor Settings

Code Indentation

Want to change your Syntax Highlighting theme, Fonts and more?

Visit your global Editor Settings.


  <section class='form-container'>
    <form id='grades'>
      <div class='entry'>
        <label for='maths'>Maths</label>
        <input id='maths' type='number' value='0' min='0' max='100'>
      <div class='entry'>
        <label for='english'>English</label>
        <input id='english' type='number' value='0' min='0' max='100'>
      <div class='entry'>
        <label for='physics'>Physics</label>
        <input id='physics' type='number' value='0' min='0' max='100'>
      <div class='entry'>
        <label for='chemistry'>Chemistry</label>
        <input id='chemistry' type='number' value='0' min='0' max='100'>
      <div class='entry'>
        <label for='biology'>Biology</label>
        <input id='biology' type='number' value='0' min='0' max='100'>
      <div class='entry'>
        <label for='history'>History</label>
        <input id='history' type='number' value='0' min='0' max='100'>
      <div class='entry'>
        <label for='geography'>Geography</label>
        <input id='geography' type='number' value='0' min='0' max='100'>
      <button id='calculate' type='submit'>Calculate</button>
    <h2 id='total'></h2>


                @import url(';700&display=swap');

*, *:before, *:after {
  box-sizing: border-box;

body {
  font-family: 'Lato', sans-serif;
  font-weight: 400;

main {
  display: grid;
  place-items: center;
  padding: 2rem 0;

.form-container {
  width: 250px;

.form-container h1, h2{
  font-weight: 700;
  text-align: center;

.entry {
  position: relative;
  width: 100%;
  display: grid;
  grid-template-columns: 100px 1fr;
  margin-bottom: 1rem;
  align-items: center;

.entry input {
  padding: .5rem;
  width: 100%;

.entry span {
  position: absolute;
  right: -25px;

button {
  margin: 1rem 0;
  padding: .5rem 1rem;
  cursor: pointer;

input:invalid {
  color: red;

input:valid {
  color: black;


                // Here we wrap the code in a function that will be called when
// all the content has been drawn/written to the page. DOM = Document Object Model.
// This has the added benefit of containing your code, keeping it out of the
// Global Namespace, so that it can't be overwritten or conflict with code elsewhere.
window.addEventListener('DOMContentLoaded', function(event) {

  // Note: I have opted for separate shorter functions
  // This makes them easier to debug, test and makes them re-usable.
  // We can re-use clampScore, or displayScore elsewhere if needed.
  // The code isn't hidden in the middle of one long function.

  // In the clamp function below max = 15, min = 0 are default values
  // and are optional e.g.
  // clampScore(17) -> 15
  // clampScore(-5) -> 0
  // clampScore(22, 20, 5) -> 20 (here we set max to 20, and min to 5)
  // clampScore(1, 20, 5) -> 5
  function clampScore(score, max = 15, min = 0) {
    return Math.max(Math.min(max, score), min);

  function calculateScore(score) {
    /* Courtesy of Archibald */
    return clampScore(2 * Math.floor(score / 10) - 3);

  function sumInputValues(inputs) {
    let total = 0;

    inputs.forEach(function(input) {
      // inputs values by default are strings 
      // So '10' + '13' = '1013' - no good!
      // convert the string to a number first
      // 10 + 13 = 23 - better!
      const inputValue = Number(input.value);

      total += calculateScore(inputValue);

    return total;

  function displayScore(score) {
    const displayScore = document.getElementById('total');
    displayScore.textContent = 'Score: ' + score;

  // the event object is passed to a handler by default when an event is fired.
  function calculateScores(event) {
    // press F12(chrome) to bring up your console
    // click on the 'Calculate' button in the preview window and check your console.
    // click on SubmitEvent and have a look at the properties.
    event.preventDefault(); // stop the form submitting to a url
    // We added an EventListener to the form below
    // the target property on the event object is the form element.
    // You can see this in the console as pointed out above.
    const form =;
    // find the inputs using the form as the root to search from
    const inputs = form.querySelectorAll('input'); 
    const total = sumInputValues(inputs);


  const form = document.querySelector('#grades'); // get the form element
  form.addEventListener('submit', calculateScores); // add a submit listener