1

I am trying to create a function that starts out with a report button and, if you click it, the page will redirect you to where you can type some words into the text box and send or you have a cancel button. If you type in a comment and press the send button then whatever you typed will show up. However, if you click the cancel button it will return you to a page that just has the report button.

While I am certain my code so far is incorrect, my main question is why doesn't anything happen after I click the report button? I based my code off this post.

Here is my code:

let reportButton;
let cancelButton;
let reporting;
let button;
//let input;

const scenes = {
  
  report: () => {
  

    cancelButton?.remove();
    // initialize idle state
    reporting = false;
    reportButton = createButton("Report");
    reportButton.position(5, 5);
    reportButton.mousePressed = () => {
      scenes.reportComment();
    };
    draw = () => {
        clear();
    };
  },

  reportComment: () => {
    
    reportButton?.remove();
    reporting = true;
    cancelButton = createButton("Cancel");
    cancelButton.position(5, 5);
    cancelButton.mousePressed = () => {
      scenes.report();
    };
    draw = () => {
      clear();
      input = createInput();
      input.position(20, 65);

      button = createButton('Submit');
      button.position(input.x + input.width, 65);
      button.mousePressed(sendReport);
      
      question = createElement('h2', 'Report');
      question.position(20, 5);

      textAlign(CENTER);
      textSize(50);
      
    };
  },

};

function sendReport() {
  const customQ = input.value();
  question.html(customQ);
  input.value('');
}

function setup() {
  createCanvas(500, 100);
  textSize(20);
  
}

function draw() {
  scenes.report();
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.5.0/p5.js"></script>
ggorlen
  • 44,755
  • 7
  • 76
  • 106
cj447
  • 21
  • 1

1 Answers1

1

Your code (overwriting p5's mousePressed method with a new, unrelated function):

reportButton.mousePressed = () => {
  scenes.reportComment();
};

Correct code (calling p5's mousePressed method, passing in a callback that represents the handler to trigger on press):

reportButton.mousePressed(() => {
  scenes.reportComment();
});

It's a subtle difference, but a close look at the example in the createButton docs shows the way.

Manipulating p5.js objects usually involves method calls rather than setting object properties with =, in case this helps you "smell" the pattern easier in the future. You may have been misled by the look of draw = () => {} and mousePressed = () => {}, but these overwrite window variables, replacing function draw() and function mousePressed() to change the main loop that p5 runs and the global mouse press handler, respectively. That's a different case.

A good debugging strategy is to eliminate all unneeded code until all that's left is the following minimal reproduction:

function setup() {
  const b = createButton("Report");
  b.position(10, 10);
  b.mousePressed = () => console.log("worked!");
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.6.0/p5.js"></script>

After removing all distractions having to do with scenes, it's much easier to figure out that mousePressed is being used incorrectly.

Your other handler, cancelButton.mousePressed, should also be adjusted.


After you fix your handlers, you'll probably notice that you can't type into your input. The reason is that it's being created many times per second because it's in the draw() loop:

draw = () => {
  // repeat this code many times a second
  clear();
  input = createInput();
  input.position(20, 65);
  // ...

Instead, put your element-creation code outside draw() so it's executed one time. For each scene, that top-level code is basically setup() for that scene. draw() is generally for animation, moving objects around the screen, and occasionally creating and destroying objects as the animation requires. Your current app doesn't need any draw()s at the current time, so I'd remove them to avoid confusion.

clear() clears the canvas, not DOM elements, so it's also not necessary in your app yet. Ditto for textAlign(CENTER); and textSize(50);.

Putting it all together:

const scenes = {  
  report: () => {
    const reportButton = createButton("Report");
    reportButton.position(5, 5);
    reportButton.mousePressed(() => {
      reportButton.remove();
      scenes.reportComment();
    });
  },

  reportComment: () => {
    const cleanup = () => {
      input.remove();
      cancelButton.remove();
      submitButton.remove();
      header.remove();
    };

    const transitionToReport = () => {
      cleanup();
      scenes.report();
    };

    const transitionToSendReport = () => {
      const value = input.value();
      cleanup();
      scenes.sendReport(value);
    };

    const cancelButton = createButton("Cancel");
    cancelButton.position(5, 5);
    cancelButton.mousePressed(transitionToReport);

    const input = createInput();
    input.position(20, 65);

    const submitButton = createButton("Submit");
    submitButton.position(input.x + input.width, 65);
    submitButton.mousePressed(transitionToSendReport);

    const header = createElement("h2", "Report");
    header.position(20, 5);
  },

  sendReport: customQ => {
    const header = createElement("h2", `report: '${customQ}'`);
    header.position(20, 5);
  },
};

function setup() {
  scenes.report();
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.6.0/p5.js"></script>

Or keeping it to two scenes:

const scenes = {  
  report: () => {
    const reportButton = createButton("Report");
    reportButton.position(5, 5);
    reportButton.mousePressed(() => {
      reportButton.remove();
      scenes.reportComment();
    });
  },

  reportComment: () => {
    const transitionToReport = () => {
      input.remove();
      cancelButton.remove();
      submitButton.remove();
      header.remove();
      scenes.report();
    };

    const cancelButton = createButton("Cancel");
    cancelButton.position(5, 5);
    cancelButton.mousePressed(transitionToReport);

    const input = createInput();
    input.position(20, 65);

    const submitButton = createButton("Submit");
    submitButton.position(input.x + input.width, 65);
    const sendReport = () =>
      header.elt.textContent = input.value();
    submitButton.mousePressed(sendReport);

    const header = createElement("h2", "Report");
    header.position(20, 5);
  },
};

function setup() {
  scenes.report();
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.6.0/p5.js"></script>

Note that I'm locally-scoping all variables to each scene. Each scene is responsible for cleaning up its own elements when transitioning to another scene. This helps manage complexity as the app grows.

UX isn't great since the user should be able to press Enter to transition to the next scene, and there should probably be some validation that the input has some content in it, but that's out of scope for this answer.

ggorlen
  • 44,755
  • 7
  • 76
  • 106
  • Hi, thanks for all your help! This information is both fascinating as it is helpful, but there still seems to be errors with my code as I got rid of the draw and clear functions and I have replaced my code with (note I have made this change with my cancelButton as well): reportButton.mousePressed(() => { scenes.reportComment(); }); but clicking on the button still does not seem to do anything. Is there something I'm still not getting? – cj447 Feb 03 '23 at 06:29
  • That looks fine and should work, but maybe something else is wrong. I added a runnable example that you can compare against. – ggorlen Feb 03 '23 at 06:52
  • So I was wondering how should I know when to add more scenes? Because my code doesn't have many scenes (and clearly doesn't work) but your code runs cleanly with slightly more scenes. Would it even be possible have the code do what I want with just the 2 scenes I had? – cj447 Feb 03 '23 at 22:15
  • You get to decide what's a scene and what isn't--it's a design choice that isn't terribly relevant to my answer. You could make `sendReport` a separate scene from `reportComment`. Or, maybe it's just a helper function that changes state within the `reportComment` scene. I arbitrarily called it a new "scene", but maybe I should have stuck closer to your vision and simply had it change the `

    ` value and clear the input within `reportComment`. My code is just an example you can modify as needed. If you do stick with your approach, I'd suggest putting `sendReport` inside its scene.

    – ggorlen Feb 03 '23 at 23:19
  • The main idea behind my "scene" concept is that you have an object of functions, each of which (a "scene") is basically a self-contained, mini p5.js sketch with its own set of handlers, variables, setup/teardown code and rendering loop. It's just an organizational pattern that's generally better than one rendering loop with a bunch of `if`s to determine which scene to render per frame. Within the pattern you can do whatever you want--there are no rules. – ggorlen Feb 03 '23 at 23:25
  • Okay I think I'm starting to understand. Thanks for your amazing help! – cj447 Feb 11 '23 at 22:17