admin管理员组

文章数量:1399963

I have defined a function plusOne() which is supposed to add +1 to a variable (and then set the innerHTML of an element to be that variable). The function is called when a button is clicked.

However, the adding of +1 only works once. How do I have to change my function in order to add +1 to the variable every time the button is clicked?

Here's how my function and HTML are looking right now, along with a JSFiddle:

function plusOne() {
  var number = 1;
  var count = document.getElementById('count');

  number++;
  count.innerHTML = number;
}
<div>
  <span id="count">1</span>
</div>

<div>
  <button onclick="plusOne()">
    +
  </button>
</div>

I have defined a function plusOne() which is supposed to add +1 to a variable (and then set the innerHTML of an element to be that variable). The function is called when a button is clicked.

However, the adding of +1 only works once. How do I have to change my function in order to add +1 to the variable every time the button is clicked?

Here's how my function and HTML are looking right now, along with a JSFiddle:

function plusOne() {
  var number = 1;
  var count = document.getElementById('count');

  number++;
  count.innerHTML = number;
}
<div>
  <span id="count">1</span>
</div>

<div>
  <button onclick="plusOne()">
    +
  </button>
</div>

Link to the JSFiddle: https://jsfiddle/johschmoll/hLymhak7/1/

Share Improve this question edited Jan 24, 2018 at 15:49 TylerH 21.1k78 gold badges79 silver badges114 bronze badges asked Jan 24, 2018 at 9:24 JoSchJoSch 9902 gold badges20 silver badges40 bronze badges 5
  • 5 Move var number = 1; outside of the function. You're resetting it to 1 each time you call the function. – Andy Commented Jan 24, 2018 at 9:25
  • 1 @Andy Ahhh, I see. Dumb mistake, should've figured that one out by myself. Thanks. – JoSch Commented Jan 24, 2018 at 9:26
  • 2 Define var number = 1; as a global scope or use closure – Abhishek Pandey Commented Jan 24, 2018 at 9:26
  • 2 you should also do the same with var count = document.getElementById('count'); you dont need to call it everytime – Neji Soltani Commented Jan 24, 2018 at 9:28
  • Close voters - this is not a typographical error. This is a conceptual problem in understanding that many people might repeat when new to programming. The typo reason is not a valid CV reason here. However, it is probably a duplicate. – TylerH Commented Jan 24, 2018 at 15:50
Add a ment  | 

3 Answers 3

Reset to default 4

Minimal solution: Move the state variable outside of the click handler's scope

Change your JavaScript to put the number variable outside of the click handler. Otherwise, you are resetting the number variable to 1 everytime the click handler is called.

var number = 1;

function plusOne() {
  var count = document.getElementById('count');

  number++;
  count.textContent = number.toString();
}

Example: https://jsfiddle/hLymhak7/6/

Move the element reference outside the click handler's scope

It is also a good idea to keep the element reference outside of the click handler's scope if the element is never destroyed.

var number = 1;
var count = document.getElementById('count');

function plusOne() {
  number++;
  count.textContent = number.toString();
}

DOM query lookups are cheap nowadays, but a lot of them will negatively affect your app's performance.

Example: https://jsfiddle/hLymhak7/8/

Make the element dependency explicit

We can even pass the count element to the click handler to make it easier to test.

JavaScript

var number = 1;

function plusOne(count) {
  number++;
  count.textContent = number.toString();
}

HTML

<div>
  <span id="count">1</span>
</div>

<div>
  <button onclick="plusOne(count)">
    +
  </button>
</div>

The span element is actually assigned to a global variable which is within the scope of the button element just like the plusOne click handler. This means that in all examples, we could just as easily have used count or window.count to access the span element.

Example: https://jsfiddle/hLymhak7/12/

Best practice: Add as event listener

It is not remended to bind the click handler by using the onclick attribute of the button element. One of the reasons is that we are only ever allowed to add one onclick handler, which is not the case with Element#addEventListener.

HTML

<div>
  <span id="count">1</span>
</div>

<div>
  <button id="incrementor">
    +
  </button>
</div>

JavaScript

var number = 1;
var count = document.getElementById('count');
var incrementor = document.getElementById('incrementor');
incrementor.addEventListener('click', plusOne);

function plusOne() {
  number++;
  count.textContent = number.toString();
}

See more discussions about onclick

Example: https://jsfiddle/hLymhak7/13/

Combine best practice with explicit element dependency

We can add a click listener that also passes the count element explicitly to the plusOne function.

var number = 1;
var count = document.getElementById('count');
var incrementor = document.getElementById('incrementor');
incrementor.addEventListener('click', function onClick() {
    plusOne(count);
});

function plusOne(count) {
  number++;
  count.textContent = number.toString();
}

Now we are one step closer to maintainable code that is easily tested.

Example: https://jsfiddle/hLymhak7/14/

Final solution that is maintainable and easily tested

We can plete our solution by making the second dependency explicit, namely the number state variable.

When we pass this variable to the plusOne function, we now have a pure function which makes it easy to test and reason about.

HTML

<div>
  <span id="count">1</span>
</div>

<div>
  <button id="incrementor">
    +
  </button>
</div>

JavaScript

var number = 1;
var count = document.getElementById('count');
var incrementor = document.getElementById('incrementor');
incrementor.addEventListener('click', function onClick() {
    number = plusOne(count, number);
});

function plusOne(count, number) {
  number++;
  count.textContent = number.toString();
  
  return number;
}

While this is more verbose, the dependendencies are clear and the actual business logic, i.e. the plusOne function, can be extracted to a separate file and unit tested to verify that it does what it is supposed to.

Test suite

import { plusOne } from './plus-one';

describe('plusOne', () => {
  let countElement;
  let initialState;
  let state;

  beforeEach(() => {
    initialState = 1;
    state = initialState;
    countElement = {
      textContent: initialState.toString(),
    };
  })

  it('returns an incremented state', () => {
    state = plusOne(countElement, state);
    expect(state).toBe(initialState + 1);
  });

  it('does not mutate the state', () => {
    plusOne(countElement, state);
    expect(state).toBe(initialState);
  })

  it('reflects the state in the count element', () => {
    state = plusOne(countElement, state);
    expect(countElement.textContent).toEqual(state.toString());
  });
});

Example: https://jsfiddle/hLymhak7/15/

Test suite example: https://stackblitz./edit/stack-overflow-javascript-jquery-repeatedly-add-1-to-variable-o?file=src%2Fplus-one.spec.ts

Anti-pattern: Keep state in DOM

A lot of web apps keep the state in the DOM. While this is easy and we have less mutable state in our code, usually we want access to the state in multiple places of our apps.

Having to extract the state from the DOM in all places where we need it is not how it is supposed to be. We are supposed to keep our business logic in JavaScript and let the DOM reflect the state, not the other way around.

It also adds to a tight coupling to the DOM, making it more difficult to maintain and test.

// Keeping state in DOM is NOT remended, but here we go...
var count = document.getElementById('count');

function plusOne() {
  var number = Number(count.textContent);
  number++;
  count.textContent = number.toString();
}

Example: https://jsfiddle/hLymhak7/9/

<!-- Using inline js-->
<div>
  <span id="count">1</span>
</div>

<div>
  <button onclick="document.getElementById('count').innerHTML++">
    +
  </button>
</div>

function plusOne(){
var count = document.getElementById('count');
count.innerHTML++
}
<div>
  <span id="count">1</span>
</div>

<div>
  <button onclick="plusOne()">
    +
  </button>
</div>

本文标签: javascriptHow to repeatedly add 1 to variable on button clickStack Overflow