Strange bug fix and time-tested debugging strategies



Remember the last time you ran into a UI error that took you hours and hours to fix? Perhaps this error occurred periodically, for no apparent reason. Maybe it appeared under certain conditions (it could depend on the device, operating system, browser or user actions) or was it hidden somewhere in the depths of one of the many front-end technologies that are part of the client side of the web project?



I recently had to remember how confusing the causes of UI errors can be. Namely, we are talking about fixing an interesting bug that affects the output of SVG images in the Safari browser. This error occurred without any specific system and for no obvious reason. When faced with a problem, I tried to find similar cases, hoping that the descriptions of such cases will give me a hint of what is happening. But I have not been able to find anything useful. True, despite all the obstacles in front of me, I was able to cope with this error.



I analyzed the problem using some of the debugging strategies that I'm going to cover in this article. After I got rid of the mistake, I remembered the advicewhich Chris Coyer gave to his Twitter readers a few years ago. This advice goes like this: "Write the article that you would like to find when you visited a search engine." In fact, that's what I did.



Problem overview



In a project I was working on on a live site, I found an error, the manifestation of which I recorded on this video. This is what the button looks like in its normal state.





Button in normal state



And here is the same button after a problem.





Part of the button is cut off



I have reproduced this error in various situations causing the page to redraw. For example, it occurred when the browser window was resized.



In order to demonstrate the problem, I created an example on CodePen . We will discuss it in more detail below. But you can experiment with this example yourself. Namely, we are talking about the fact that if you open this example in the Safari browser, then, when the page loads, the buttons will look as expected. But if you click on one of the two larger buttons, the bug sticks out its ugly head.





Why is the SVG image cropped?



Every time the event occurspaint, the SVG images used in the larger buttons are not displayed correctly. These images are simply cropped. This can happen, for no apparent reason, on page load. This can happen even when the window is resized. In general, the error appears in a variety of situations.



An overview of the project in which the error occurred



I think that when talking about the error, it would be good to reveal details about the project and the conditions in which it occurs.



  • The project uses React (but the reader of this article does not need to know React in order to understand it).
  • SVG images are imported into the project as React components and embedded in HTML using webpack.
  • . .
  • CSS.
  • , , HTML- <button>.
  • Safari ( 13 ).




Let's look at the error and consider whether we can make any assumptions about what is going on. The reasons for such errors usually do not lie somewhere on the surface, therefore, faced with such errors, one cannot immediately say with confidence what is happening. In our first attempt to understand a problem, we do not have to strive to identify its cause with 100% accuracy. We will investigate the error step by step, formulating and testing hypotheses that will help us narrow down the list of possible causes of what is happening.



Formulating a hypothesis



At first glance, what is happening looks like a CSS error. Perhaps, when you hover the mouse over the button, some styles are applied to it, which break the layout. Perhaps the overflowSVG image attribute is to blame . In addition, there is a feeling that an error occurs without any specific system when the page is redrawn for various reasons (an event paintwhen the browser window is resized, when the mouse pointer is over a button, when it is clicked, and so on).



Let's start with the simplest and most obvious assumption. Let's say the error is in the CSS. We can assume that there is a bug in the Safari browser that results in incorrect SVG output when specific styles are applied to SVG elements. For example, like the styles used to build flex layouts.



We have just formulated a hypothesis. Our next step is to conduct a test that will either confirm or disprove this hypothesis. The result of each test will give us new information about the error and will help in formulating the following hypotheses.



Simplifying the problem



We will use a debugging strategy called "simplifying the problem". This will allow us to pinpoint where the error occurred. In one lecture on computer science at Cornell University, this strategy is described as "an approach to gradually get rid of code that is not related to error."



Assuming that the error is in the CSS, we can eventually either find the cause of the error or eliminate the CSS from the equation, which will reduce the number of possible causes of the error and reduce the complexity of the problem.



Let's test our hypothesis. Let's try to confirm it. In this case, if you temporarily disable all non-standard styles from the page, this should result in the error no longer appearing.



Here is the code to include the appropriate stylesheet:



import 'css/app.css';


I created this CodePen project to demonstrate the output of elements without CSS . In React, SVG graphics are imported into a project as a component , then the corresponding code is embedded in HTML using webpack.





Normal Button View



If you open the aforementioned project in Safari and click on one of the large buttons, it turns out that the error has not gone anywhere. It also happens when the page is loaded, but when using CodePen, you need to click on the button to trigger an error.





The error did not disappear even when CSS was disabled (Safari 13)



As a result, we can conclude that CSS has nothing to do with it. However, we can pay attention to the fact that in such conditions only two of the five buttons are displayed incorrectly. Let us remember this and move on to the next hypothesis.



Error isolation



Our next hypothesis is that Safari has a bug when rendering SVG images inside HTML elements <button>. Since the problem arises when the first two buttons are displayed, we isolate the first of them and see what happens.



Sara Drazner in thisThe material explains the importance of insulation. I highly recommend reading this material for anyone interested in more details about debugging tools and different approaches to finding bugs. Here is a quote from that material: “Isolation is perhaps the most important basic principle of debugging. The code that works in our projects can be scattered across different libraries and frameworks. Many people may be involved in the work on projects, and some of those who contributed to the development of projects no longer work on them. Isolating the problem helps us to slowly cut off what is not causing the error to appear. This allows, in the end, to find the source of the problem and focus on it. "



Fault isolation is often referred to as a " shorthand test case ".



I moved the button to a separate blank page (created a separate test for it). Namely, this CodePen project was created to investigate a button in isolation. While we have concluded that CSS is not the cause of the problem, we need to leave styles disabled until we discover the real cause of the problem. This will allow us to simplify the problem as much as possible.





A page with only a button



If you open this project in Safari, it turns out that we can no longer cause an error. Even after clicking the button, the image does not change. But this cannot be considered an acceptable solution to the problem. However, the code for this CodePen project gives us an excellent base for creating a minimal reproducible example.



Minimal reproducible example



The main difference between the two previous CodePen projects is the button combination. Examining all the possible button combinations allows us to conclude that the problem only occurs when an event paintoccurs for a larger SVG image next to which a smaller SVG image is located on the page.



Knowing this allowed us to create a minimal, reproducible example that allowed us to reproduce the error and still get rid of any unnecessary elements. Thanks to a minimal reproducible example, we can study the problem more deeply and highlight exactly the part of the code that causes it.



Here is the CodePen project in question.





Minimal reproducible example



If we open this project in Safari and click on the button, we again run into a problem. This will allow us to formulate a hypothesis that the two SVG elements somehow conflict with each other. If you overlay the second SVG image on top of the first, it turns out that the size of what remains of the background of the first image exactly matches the size of the smaller image.





A drawing in which the second image is placed on top of the first, distorted as a result of an error



Divide and rule



We were able to reproduce the bug using the minimum number of elements represented by a pair of SVG images. Now we're going to further narrow down the scope of the problem, narrowing it down to the specific piece of SVG code that is the source of the problem. If we understand SVG code in general terms and still want to find the source of the problem, then we can use a binary tree search strategy using a divide and conquer approach. Here is another extract from the lecturefrom Cornell University Computer Science: “For example, you can start by examining a large piece of code and place the validation code in the middle of the piece you are examining. If an error does not occur here, it means that its source is in the second half of the code. Otherwise, its source is in the first half of the code. "



When examining the SVG code, you can try to remove an element <filter>from the description of the first image (and also <defs>, since there is nothing in this block anyway). Let's take an interest in what kind of tasks the element solves <filter>. A great explanation for this can be found here . Namely, we are talking about the following: “To apply filters to SVG images, there is a special element called<filter>... It essentially resembles elements designed to work with linear gradients, masks, templates, and other graphic effects. The element is <filter>never displayed on its own. It is only used as something that can be referenced using an attribute filterin SVG code or a function url()in CSS. "



In our SVG image, a filter is used to add a small inner shadow at the bottom of the image. After removing the filter from the code of the first image, we wait for this shadow to disappear. If after this the problem persists, then we can conclude that something is wrong with the other code for describing the SVG element.



I created another CodePen project to demonstrate the results of this test.





Consequences of removing the <filter> element



The problem, as you can easily see, has not gone anywhere. And the inner shadow continues to be displayed even after removing the filter code. But now, among other things, the problem appears in all browsers. This allows us to conclude that the error is somewhere in the rest of the button description code. If you remove the restidfrom<g filter="url(#filter0_ii)">, then the shadow disappears. What's going on here?



Let's take another look at the above definition of an element<filter>and notice the following words: “An element is<filter>never displayed on its own. It is only used as something that can be referenced using an attributefilterin SVG. " (I highlighted a fragment of the text.)



So, knowing this, we can conclude that the filter declaration from the second SVG image is applied to the first SVG image, which leads to the error.



Bug fix



We now know that our problem is element related <filter>. We also know that both SVG images have this element, since the filter is used to create a circular inner shadow. Let's compare the code of two SVG images and think about whether we can explain the error and fix it.



I have simplified the code of both images so that you can clearly see what is happening in it.



Here is the code for the first SVG image:



<svg width="46" height="46" viewBox="0 0 46 46">
  <g filter="url(#filter0_ii)">
    <!-- ... -->
  </g>
  <!-- ... -->
  <defs>
    <filter id="filter0_ii" x="0" y="0" width="46" height="46">
      <!-- ... -->
    </filter>
  </defs>
</svg>


Here is the code for the second image:



<svg width="28" height="28" viewBox="0 0 28 28">
  <g filter="url(#filter0_ii)">
    <!-- ... -->
  </g>
  <!-- ... -->
  <defs>
    <filter id="filter0_ii" x="0" y="0" width="28" height="28">
      <!-- ... -->
    </filter>
  </defs>
</svg>


Analyzing these two fragments, you can notice that id=filter0_iithe same identifier is used in the construction . Safari applies the last browser parsed filter definition to the elements (in this case, the filter of the second image). This leads to the fact that the first image is cropped. Its original size is 48px, and after applying the filter, a piece is cut out of it 26px. The idDOM property must have a unique value. If there are several identical ones on the page id, the browser cannot figure out which one it needs to use. And since the property is filteroverridden when each event occurspaint, then, depending on which of the definitions will be ready first (something like a race condition occurs here), the error either appears or not.



Let's try to assign unique values idin the code of each of the images and see the results. Here is the corresponding CodePen project.





Assigning unique ids solved the problem



If you now open the project in Safari and click the button, you can be sure that we solved the problem by assigning uniqueidfilters used in SVG images. If you think about the fact that the project had non-unique values ​​for an attribute likeid, it will lead to the conclusion that the problem should have appeared in all browsers, not just in Safari. But for some reason, other browsers (including Chrome and Firefox) seem to have handled this unusual situation without error. It could be a coincidence, though.



Outcome



It was another adventure! We started knowing only that there was some kind of error in the project, which sometimes appears and then does not, and in the end we fully understood the reasons for what was happening and coped with the problem. Debugging user interface code and figuring out why graphics are distorted can be tricky if you don't understand what is happening. Luckily for us, there are debugging strategies that can help us find the root cause of even the most confusing errors.



First, we simplified the problem by formulating hypotheses that allowed us to remove components from the project that were not related to the error (styles, markup, dynamic events, etc.). After that we isolated the markup and found a minimal reproducible example. This allowed us to focus on a small piece of code. And we finally identified the problem using a divide and conquer strategy to get rid of the error.



Thanks to everyone who took the time to read this article. But, before we end the conversation, let me tell you about another debugging strategy mentioned in the lectures.Cornell University. The point is that in the process of work you need to take breaks, rest and free your head from all thoughts: “If it takes too much time to debug, then the programmer gets tired. It may turn out that he, being in such a state, works in vain. In a situation like this, it's worth taking a break and throwing everything out of your head. And after a while you should try to look at the problem from a different point of view. "



How do you fix incomprehensible errors?






All Articles