Search code examples
javascripthtmlcssrepaintreflow

Reflow/repaint issues? Optimize app that is way too slow


Alright so I have run into what I believe is an optimization issue. I have written a short app that grabs data from two fields, rips them apart character by character and matches them to each other, highlighting discrepancies. The issue seems to be that because the function loops through and not only prints each character one at a time, but changes color in a new span one at a time, there is a severe amount of bottlenecking for large data entries. One sentence of even five work just fine, but when you get up to a full page of text, things bog down and in some cases crash. I have tried to look up some fixes for repaints/reflows, but I haven't run into this issue before and don't know much about it. ANy tips welcome.

Code is here:

HTML:

    <!DOCTYPE html>
<html>
  <head>
    <title>Data Comparison</title>
  </head>
  <body>
    <p><strong>Insert text into fields one and two and press 'Compare'. Matching data is green non-matching is red. </strong></p>
    <div class="userField">
      <h1>Input Field 1</h1>
      <textarea id="textAreaOne" rows="30" cols="65"></textarea>
    </div>

    <div class="userField">
      <h1>Input Field 2</h1>
      <textarea id="textAreaTwo" rows="30" cols="65"></textarea>
    </div>
    <div id="submission">
    <button onclick="compare(textAreaOne);">Compare</button>
    </div>
    <div id="divOne"> 
      <h1 id="titleOne">Output Field 1</h1>
      <p id="outputOne"></p>
    </div>
    <div id ="divTwo">
      <h1 id="titleTwo">Output Field 2</h1>
      <p id="outputTwo"></p>
    </div>
  </body>
</html>

CSS:

body {
  width: 100%;
  margin: 0;
  padding: 0;
  font-family: Verdana, Geneva, sans-serif;
  font-size: 15px;
}

.userField {
  display: inline-block;
  width: 44%;
  margin: 15px;
  padding: 15px;
  background-color: lightgrey;
  border-radius: 13px;
  box-shadow: 3px 3px 3px slategrey;
}

#submission {
  text-align: center;  
}

#divOne {
  display: inline-block;
  margin: 15px;
  padding: 15px;
  width: 44%;
  word-wrap: break-word;
  background-color: lightgrey; 
}

#divTwo {
  display: inline-block;
  background-color: lightgrey;
  width: 44%;
  word-wrap: break-word; 
  margin: 15px;
  padding: 15px;
}

#titleOne {
  background-color: white;
  width: 240px;
  border-radius: 30px;
  padding: 6px;
}

#titleTwo {
  background-color: white;
  width: 240px;
  border-radius: 30px;
  padding: 6px;
}

JS:

const fieldOne = document.querySelector("#textAreaOne");
const fieldTwo = document.querySelector("#textAreaTwo");


function compare(){
  document.querySelector("#outputOne").innerHTML = "";
  document.querySelector("#outputTwo").innerHTML = "";
  document.querySelector("#divOne").style.visibility = "hidden";
  document.querySelector("#divTwo").style.display = "hidden";

  let dataOne = [];
  let arrOne = fieldOne.value;
  let temp = arrOne.split("");
    dataOne.push(temp);
  let dataTwo = [];
  let arrTwo = fieldTwo.value;
  let tempTwo = arrTwo.split("");
    dataTwo.push(tempTwo);

  if (fieldOne.value.length <= fieldTwo.value.length){
    for (var i = 0; i<fieldOne.value.length; i++){
      if (dataOne[0][i] === dataTwo[0][i]){
 document.querySelector("#outputOne").innerHTML += "<span style='color:green'>" + dataOne[0][i] + "</span>";
 document.querySelector("#outputTwo").innerHTML += "<span style='color:green'>" + dataTwo[0][i] + "</span>";
      } else {
        document.querySelector("#outputOne").innerHTML += "<span style='color:red'>" + dataOne[0][i] + "</span>";
        document.querySelector("#outputTwo").innerHTML += "<span style='color:red'>" + dataTwo[0][i] + "</span>";
     }

    }
    if (fieldOne.value.length < fieldTwo.value.length){document.querySelector("#outputTwo").innerHTML += "<span>...</span>";}
  } 

  else {
    for (var i = 0; i<fieldTwo.value.length; i++){
      if (dataOne[0][i] === dataTwo[0][i]){

 document.querySelector("#outputOne").innerHTML += "<span style='color:green'>" + dataOne[0][i] + "</span>";
 document.querySelector("#outputTwo").innerHTML += "<span style='color:green'>" + dataTwo[0][i] + "</span>";
      } else {
         document.querySelector("#outputOne").innerHTML += "<span style='color:red'>" + dataOne[0][i] + "</span>";
        document.querySelector("#outputTwo").innerHTML += "<span style='color:red'>" + dataTwo[0][i] + "</span>";
      } 
    }
 if (fieldTwo.value.length < fieldOne.value.length){        document.querySelector("#outputOne").innerHTML += "<span>...</span>";}
  }

  document.querySelector("#divOne").style.visibility = "visible";
  document.querySelector("#divTwo").style.visibility = "visible";
}

https://codepen.io/Axfinger/pen/QxvbqM?editors=0010

Thanks


Solution

  • The main reason your code is slow it's because you keep modify innerHtml for every letter.

    This approach is way faster even with several paragraph:

    [...]
    
    let outOne = '';
    let outTwo = '';
    
    if (fieldOne.value.length <= fieldTwo.value.length){
      for (var i = 0; i<fieldOne.value.length; i++){
        if (dataOne[0][i] === dataTwo[0][i]){
          outOne += dataOne[0][i];
          outTwo += dataTwo[0][i];
        } else {
          outOne += "<span style='color:red'>" + dataOne[0][i] + "</span>";
          outTwo += "<span style='color:red'>" + dataTwo[0][i] + "</span>";
        }  
      }
      if (fieldOne.value.length < fieldTwo.value.length){outTwo += "...";}
    }
    
    [...]
    
    outputOne.innerHTML = outOne;
    outputTwo.innerHTML = outTwo;
    

    Note that I omitted parts of the code for clearness.

    That said if you still need more speed:

    1. Lookup for fitting as much data as possible into the red spans. That is the whole consecutive subsequence of different letters.
    2. Execute the function in a webworker (Will take similar time but at least won't freeze the browser).