📜 ⬆️ ⬇️

Pure CSS3 Calculator

It was a rather interesting project. I tried to create an arithmetic calculator purely on CSS3 (and not JavaScript). Using elements such as calc (), attr (), counter (), etc., this seemed like a not so difficult task, but it turned out to be not so simple.

Before I begin, I would like to note that there are no reasonable reasons to create a calculator using only CSS , no. I did it just for fun.

Stable only in Firefox 4 and IE 9
')
One of the key components of any calculator is the ability to convert inputs. Using only CSS, we have very limited options for capturing input. Thus, checkboxes are used to register all inputs. To apply changes to other elements, you can use the “: checked” state and the “~” selector, and since they are fairly straightforward, I will not go into details, but instead focus on the logic of calculating values.

Registering the first key is really not a problem, since if you click on the checkbox with the number 9, you register the input of the number 9. The first problem occurs if the user wants to enter 95 (ie, 9 and 5). If we have already registered 9, we can not just add 5 (since it will be 14). We can multiply by 10, but then it will turn out 90. We need to multiply by 10 and add 5. Therefore, counter () doesn't seem to be involved yet.

One way to achieve this is to use the font size. The advantage of font size is that the children inherit the values ​​of the parent elements, and you can set the values ​​both in percentage and in pixels.

Imagine the following HTML structure:

#div1 > #div2 > #div3 > #div4 > #div5 


When you select the first number (in this case 9), you will use the content property to add 9 characters to div5 with a font size of 1 pixel (in other words, the width of div5 will be 9 pixels). Now, when the user selects any other number, we will apply the font size: 1000% to the actual # div5 value (note that the 9 characters all also have their own font size of 1 pixel), which will result in # div5 will now be 90 pixels wide. In addition, the second number will be added as content in # div4 in the same way as in # div5, with a font size of 1 pixel, that is, the content of the text in # div4 will be 5 pixels, but including the child element # div5, the total width will be equal to 95 pixels!

We will do the same with the third number, applying a 1000% font size to the element # div4, etc. This all sounds good, but the problem is that the maximum font size is relatively small. I say “relatively” compared to the following method, which will lead to the fact that this method will be suitable only for numbers less than 100,000 or so (I don’t remember exactly what the limit in FF / Chrome was, but it seemed to me quite low, to abandon this idea).

For small numbers, this method is quite suitable (for example, as done in this blackjack , made purely on CSS3 (works only in Chrome)).

Therefore, the use of font size as an input container was not even discussed. I decided to try the calc () element. Unfortunately, webkit does not support it yet, and as an example it only works on IE9 and Firefox 4 and higher. As in the case of the font size method, we will use the same HTML structure with “div” elements placed one on one, but this time we will start with the parent element.

When you select the first number, we specify the value in the form of width # div1. When choosing the second number, we set the width for # div2 in the form of calc (1000% + number2). As in the previous example, the width of # div1 will be equal to 9 pixels, and the width of # div2 - calc (1000% + 5px), which will lead to the fact that the width will be equal to 95 pixels. In the same way, we can continue for # div3 ... # div5, assuming that the user enters up to 5 characters.

Well, we have successfully saved the value entered by the user (and we haven’t done the calculations that the user wants to do). Let's assume that the user has entered 9146, which will result in the following width of the div elements:

# div1 9px
# div2 91px
# div3 914px
# div4 9146px
# div5 9146px (default 100%)

At this time, the user decided to multiply (we removed the possibility for the user to enter new numbers before performing the action, which is currently a multiplication).

Now we need to adjust the first value (width # div5), multiplying it by any number that the user chooses. The first thing you can assume is to continue using the same method as in the first example, i.e. change structure to

# div1> # div2> # div3> # div4> # div5> # div6> # div7> # div8> # div9, etc.

then change the width using the value selected by the user. Unfortunately, this is not the case. Imagine that in the same example the user wants to multiply 9146 * 25.

That is, after the user enters the first character of the number by which he will multiply, i.e. “2”, width # div6 will be equal to 200%, which will result in 18,292? It looks pretty good and right, and so it is.

However, when we turn to the second character, we are probably facing the greatest problem in this entire application. We cannot simply multiply by 5, i.e. 500%, since 500% of 18,292 is 91,460, which is not the same as 25 * 9146, which is equal to 228,650. Performing adding multiplication using calc () will not help us either, since the real problem is that we cannot multiply the same value several times. We need an integer (25), and it should be used to multiply the original number (9146).

Another problem is that # div1 does not fix the width of the child element, so when the child element # div5 is 9146px wide, # div1 is still 9px, even though # div5 is inside it. In the case of the font size, it did not matter.

If # div1 reflects the real width, then 5 divs can be cloned 5 times and placed one behind the other, forming the following structure:

#container> {
# parent1> # div1> # div2> # div3> # div4> # div5
# parent2> # div1> # div2> # div3> # div4> # div5
# parent3> # div1> # div2> # div3> # div4> # div5
# parent4> # div1> # div2> # div3> # div4> # div5
# parent5> # div1> # div2> # div3> # div4> # div5
}

First, only one div element will be displayed (# parent1), and as soon as the first number to be multiplied is selected, we multiply the width of the div by 2, which will cause the container to be 2x9146 wide. When the second number is selected, we will again multiply # parent1 by 1000%, as a result of which the width of the container will be equal to 20x9146, after which we will display # parent2 and multiply it by 500%, with the result that the width of the last #container will be equal to 20x9146 + 5x9146 , that is, 25x9146, and so with all subsequent numbers. However, this cannot be done without the font size method.

Having tried a huge number of options with tables and other nested elements, I came to the use of selectors for fixing the number. I discarded this idea and continue to do so, as it violates the purpose of almost everything I tried to do here (none of this is needed if you simply make thousands of different css selectors for each separate combination of numbers).

That is, you get the following:

- when the user selects 2, this multiplies the width of # div5 by 200%;

- if the user selects another digit (ie, 5), we have a selector that fixes that “digit1number2: checked” is followed by “digit2number5: checked”, which then registers that the width is 2500%.

Obviously, this leads to the need to have a selector for each individual number. Therefore, if we want to be able to multiply by any number from 0 to 99, we need 100 different selectors for each individual combination. In the same way, if we want 0-999, we need 1000 selectors, and for 0-9999, 10,000 selectors, so making a calculator using this method is just silly. Despite the fact that I have run out of ideas and in view of the existence of restrictions (which I will discuss later), this is not necessarily a big loss. Anyway, we can technically provide any length of the first number, having only a few selectors, but the second number will now be limited to 0-99.

Great, we were finally able to calculate the width of the div based on the value entered by the user. How now to present it to the user? Perhaps now you are hoping that the attr () element will allow us to display the css values ​​specified in the style sheets, but unfortunately this is not the case. As already explained earlier, using the counter () element does not bring a real result, especially with regard to multiplication and division, while its presentation would be much simpler.

So how does it show the width? Remember how all previous examples were used to adjust the width of a div element? Connect this floating frame width to 100% width and multiple media queries. Great, now we have a floating frame with a width of 228,650 pixels inside the div element, and we need to make 228,650 different media queries to represent each individual number based on the width of the document? Again, this, of course, can be done, but it simply deprives all meaning.

No, instead we will use the number of floating frames, one for each digit of the number. That is, if we want to represent a quantity consisting of 6 characters, we will need 6 floating frames inside the frame we want to represent. The first frame will determine the number of characters, that is, you get something like this:

0 <width: 100: 1-2 characters
100 <width <1000: 3 characters
1000 <width <10000: 4 characters
10,000 <width <100,000: 5 characters
100,000 <width <1,000,000: 6 characters
etc.

and after that the desired frame will be displayed, which in this case is a 6-digit frame.

After that, this frame will include 10 media queries that will verify that the first character of the number is something like this:

 @media(min-width: 100px) and (max-width: 100099px) -> 0 @media(min-width: 100100px) and (max-width: 200099px) -> 1 @media(min-width: 200100px) and (max-width: 300099px) -> 2 


Each media query after this adds this number and reduces the width of the floating frame by the required number of hundreds of thousands, in this case 200,000, so that the next embedded frame is digit5.html and its width is 28,650 (since we deleted 200,000 from its 100%). After that we will use the same method to check the 5th sign, display this sign and reduce the width of the next frame to the required number of tens of thousands, and so on until the last sign.

You may have noticed that the minimum width is actually 100, not 0, and the maximum width is 100 more than you expected. This is because a floating frame with a width of 0 will not display any content (because it is 0 pixels wide!), So we need to allocate a certain space where the numbers will be displayed.

All this will lead to the appearance of a certain number of frames, which reflects the width of the document, and you can test it here . Note that it is 100 pixels smaller than the width of your window, and only works in browsers that support calc ()).

Fine! Now we have a working calculator! I mentioned that there are other limitations. It is not surprising that the width of the document / css property has a maximum value, and such a maximum value that can be calculated is:

- Firefox 17895698
- Internet Explorer 1533816

Therefore, even if we had the opportunity to enter more than two characters in the second number, using this method for multiplication we would very quickly reach such a limiting value of the property.

In addition, as you expected, any fractional parts are not accepted and displayed (they are simply rounded when dividing), and you cannot enter a negative value (I do not see any problems in correcting this).

While working on this problem, I encountered an interesting memory leak in IE when using floating frames and media queries, which causes the browser to hang on several lines of CSS and HTML.

Source: https://habr.com/ru/post/255511/


All Articles