Simple Calculator

The source code is all there in the demo project

Learning Points

The layout is kept simple to scale to a single screen on a mobile (or browser window) by using vh height property. There are 20 buttons laid out in a four column grid (each column in 25%) and each of these is 12vh high - so the buttons on the calculator take up 62% of the viewport height (because of the borders) and the calculation and its result each take up 14vh (14%) - ie the total calculator is around 90% of the viewport height and won't overflow until about 250px height (the minimum width for the viewport is about 350px).

Data binding is just to two variables - the calculation being entered at the top by pressing the calculator buttons, and the result of the calculation if what is entered can be evaluated.

var vm = new Vue({
    el: '#app',
    data: {
        inputDisplay: '',
        resultDisplay: ''
    },
1
2
3
4
5
6
  • made the buttons not selectable by the user user-select: none so that the on click works reliably. Without this property (as I recall it now) the it is possible for a mouse user in particular to click on elements of the SVG and the onclick event will not fire. It is possible to add the onclick event to the elements of the SVG but that is not wanted behaviour in this case.
  • in order to get the value of the button being pressed I added a data property to each button to indicate the character to be added to the calculation string: for example data-button="+". The value of the data property can be retrieved from the dataset property of the event object.
methods: {
    calculatorInput: function (event) {
        if (event) {
            this.inputDisplay = this.inputDisplay + event.target.dataset.button;
            displayResult();
        }
    },
1
2
3
4
5
6
7
  • the calculation is done by using javascripts built in eval function, which should be safe since the buttons are the only way to add characters to the calculation string. The eval is in a try, catch statement as not all strings of characters can be evaluated as a calculation of course, for example 65++1 fails to evaluate and the result is set to blank.
  • during testing when results were too long to display I considered rounding to avoid this, but that seemed arbitrary. I wanted a way to see if what was being displayed was too wide for the element width and then to do something about it. The nomenclature of these properties in the DOM seem a little odd to me but the code below seems to work - if the scrollWidth is bigger than the width available then the result is converted to an exponent so that it takes up much less width. Am aware that multiplying a string by 1 to get a numeric is very loose but I don't see the problem with it myself - if the result is bigger than the width available its because its a numeric string...
updated: function () {
    if(document.getElementById("displayResult").scrollWidth > document.getElementById("displayResult").offsetWidth){
        this.resultDisplay = (this.resultDisplay * 1).toExponential(4);
    }
},

1
2
3
4
5
6

The offsetWidth of an element includes borders, scroll bars and all - i.e. the whole layout width although it would be safer for general use to use getBoundingClientRect() - see Mozilla documentation as the function takes any transforms into account and returns the rendered dimensions of the object:

DOMRect { 
    x: 22.75,
    y: 100.81666564941406, 
    width: 409.5, 
    height: 92.81666564941406, 
    top: 100.81666564941406, 
    right: 432.25, 
    bottom: 193.63333129882812, 
    left: 22.75 }
1
2
3
4
5
6
7
8
9