Search code examples
javascriptjqueryhtmltoggleevent-propagation

How to stop Jquery function from running?


Newbie here. Probably because I'm already sleepy and can't find a solution. I'm trying to stop a function when I click on another button. Here's the code so far:

When I click on a button (.start), it runs a function that messes up the text randomly without finish. But then, I want to click on button #stop and stop that specific function from running. I've tried toggles, preventDefault, event.stopPropagation(), setInterval, etc... None of them works. I've tried putting inside my function and outside as well.

Here's the current js code:

$(".start").click(function(){
    var getTextNodesIn = function(el) {
    // Look at all the page elements and returns the text nodes
    return $(el)
      .find(":not(iframe,script)")
      .addBack()
      .contents()
      .filter(function() {
        return this.nodeType == 3; // Text node types are type 3
      });
  };

  // var textNodes = getTextNodesIn($("p, h1, h2, h3","*"));
  var textNodes = getTextNodesIn($("p"));

  function isLetter(char) {
    return /^[\d]$/.test(char);
  }

  var wordsInTextNodes = [];
  for (var i = 0; i < textNodes.length; i++) {
    var node = textNodes[i];

    var words = [];

    var re = /\w+/g;
    var match;
    while ((match = re.exec(node.nodeValue)) != null) {
      var word = match[0];
      var position = match.index;

      words.push({
        length: word.length,
        position: position
      });
    }

    wordsInTextNodes[i] = words;
  }

  function messUpWords() {
    for (var i = 0; i < textNodes.length; i++) {
      var node = textNodes[i];

      for (var j = 0; j < wordsInTextNodes[i].length; j++) {
        // Only change a tenth of the words each round.
        if (Math.random() > 1 / 10) {
          continue;
        }

        var wordMeta = wordsInTextNodes[i][j];

        var word = node.nodeValue.slice(
          wordMeta.position,
          wordMeta.position + wordMeta.length
        );
        var before = node.nodeValue.slice(0, wordMeta.position);
        var after = node.nodeValue.slice(wordMeta.position + wordMeta.length);

        node.nodeValue = before + messUpWord(word) + after;
      }
    }
  }

  function messUpWord(word) {
    if (word.length < 3) {
      return word;
    }

    return word[0] + messUpMessyPart(word.slice(1, -1)) + word[word.length - 1];
  }

  function messUpMessyPart(messyPart) {
    if (messyPart.length < 2) {
      return messyPart;
    }

    var a, b;
    while (!(a < b)) {
      a = getRandomInt(0, messyPart.length - 1);
      b = getRandomInt(0, messyPart.length - 1);
    }

    return (
      messyPart.slice(0, a) +
      messyPart[b] +
      messyPart.slice(a + 1, b) +
      messyPart[a] +
      messyPart.slice(b + 1)
    );
  }

  // From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random
  function getRandomInt(min, max) {
    return Math.floor(Math.random() * (max - min + 1) + min);
  }

  setInterval(messUpWords, 50);

});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="controls pt-5">
<button class="start">Simulate</button>
<button id="stop">Stop</button>
</div>
<br>
<br>
<br>
<div>
  <h1>Trying</h1>

  <p>Friends who have dyslexia described to me how they experience reading. She <em>can</em> read, but it takes a lot of concentration, and the letters seems to "jump around".</p>
</div>

The function works pretty well. I'm just having a hard time making another button stop this function from running... Any help would be appreciated and I wish y'all a nice week.


Solution

  • You cannot stop a function from running because only one function can ever run at a time within the JavaScript runtime environment (single-threaded). When you click the button to try to stop the first function, what actually happens is that the first function finishes and then the click event associated with the button runs. And, by then, the first function has completed, so it's too late to stop it.

    You can look into asynchronous operations, which run outside of the JavaScript runtime. In your case, using setInterval() to run a function repeatedly at regular intervals would probably be the best way to go. This would allow you to check between invocations to see if something else has occurred, which should stop the function from running at the next interval.

    Now, you are using the interval timer, but you aren't setting up a reference to it, so you can't tell it to stop when you need it to. The solution is to associate a variable with the timer and then to call clearInterval() and pass it the timer reference as shown here:

    let timer = null; // This will hold a reference to the timer so you can stop it later
    
    $("#stop").on("click", function(){
      clearInterval(timer); // Stop the timer
    });
    
    $(".start").click(function(){
       var getTextNodesIn = function(el) {
       // Look at all the page elements and returns the text nodes
       return $(el).find(":not(iframe,script)")
                   .addBack()
                   .contents()
                   .filter(function() {
                      return this.nodeType == 3; // Text node types are type 3
                    });
      };
    
      // var textNodes = getTextNodesIn($("p, h1, h2, h3","*"));
      var textNodes = getTextNodesIn($("p"));
    
      function isLetter(char) {
        return /^[\d]$/.test(char);
      }
    
      var wordsInTextNodes = [];
      for (var i = 0; i < textNodes.length; i++) {
        var node = textNodes[i];
    
        var words = [];
    
        var re = /\w+/g;
        var match;
        while ((match = re.exec(node.nodeValue)) != null) {
          var word = match[0];
          var position = match.index;
    
          words.push({
            length: word.length,
            position: position
          });
        }
    
        wordsInTextNodes[i] = words;
      }
    
      function messUpWords() {
        for (var i = 0; i < textNodes.length; i++) {
          var node = textNodes[i];
    
          for (var j = 0; j < wordsInTextNodes[i].length; j++) {
            // Only change a tenth of the words each round.
            if (Math.random() > 1 / 10) {
              continue;
            }
    
            var wordMeta = wordsInTextNodes[i][j];
    
            var word = node.nodeValue.slice(
              wordMeta.position,
              wordMeta.position + wordMeta.length
            );
            var before = node.nodeValue.slice(0, wordMeta.position);
            var after = node.nodeValue.slice(wordMeta.position + wordMeta.length);
    
            node.nodeValue = before + messUpWord(word) + after;
          }
        }
      }
    
      function messUpWord(word) {
        if (word.length < 3) {
          return word;
        }
    
        return word[0] + messUpMessyPart(word.slice(1, -1)) + word[word.length - 1];
      }
    
      function messUpMessyPart(messyPart) {
        if (messyPart.length < 2) {
          return messyPart;
        }
    
        var a, b;
        while (!(a < b)) {
          a = getRandomInt(0, messyPart.length - 1);
          b = getRandomInt(0, messyPart.length - 1);
        }
    
        return (
          messyPart.slice(0, a) +
          messyPart[b] +
          messyPart.slice(a + 1, b) +
          messyPart[a] +
          messyPart.slice(b + 1)
        );
      }
    
      // From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random
      function getRandomInt(min, max) {
        return Math.floor(Math.random() * (max - min + 1) + min);
      }
    
      timer = setInterval(messUpWords, 50); // Set interval reference to variable
    
    });
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
    <div class="controls pt-5">
    <button class="start">Simulate</button>
    <button id="stop">Stop</button>
    </div>
    <br>
    <br>
    <br>
    <div>
      <h1>Trying</h1>
    
      <p>Friends who have dyslexia described to me how they experience reading. She <em>can</em> read, but it takes a lot of concentration, and the letters seems to "jump around".</p>
    </div>