1

I have the following code

function setSize() {
    canvas.width = window.innerWidth;
    canvas.height = window.innerHeight;
    document.addEventListener('resize', setSize)
}

As you can see the resize event listener recursively calls setSize(). I did this so that I could handle initial size setup and window resizing in the same function.

The problem is that every recurse will add an additional eventListener, rather than replacing the last. So I need to remove the resize event listener on each recurse to avoid them stacking and eventually having dozens of event listeners triggering on window resize.

The documentation says that the removeEventListener() must take an event parameter that defines the condition upon which it will trigger. I don't want this, as I want it to trigger the moment the code is read at the beginning of the function. Like so

function setSize() {
    document.removeEventListener(setSize) // I want something like this
    canvas.width = window.innerWidth;
    canvas.height = window.innerHeight;
    document.addEventListener('resize', setSize)
}

Is there a way to do this, or some alternative method that you recommend?

Edit: The motivation behind my question is that I'd like an elegant single function solution that handles both initial setup and later window resizes, rather than having to define setSize(), call it, then create an event listener that also calls it.

function setSize() {
    canvas.width = window.innerWidth;
    canvas.height = window.innerHeight;
}

// I do not want to have to do this:
setSize()
document.addEventListener('resize', setSize)
// I want something more elegant that handles both initial setup and window resize.

I quickly realized after I posted this question that the reason why I must specify the event on removeEventListener() is because that's the specific event that setSize() is bound to trigger on. I thought that it was saying it would remove the event listener only when the event triggers, rather than immediately removing the event listener which is what I want.

Michael Moreno
  • 947
  • 1
  • 7
  • 24
  • You need to (1) specify the event for which you're removing the listener (2) pass the handler as a function to `addEventListener`, not invoke the handler immediately and then pass the undefined result (3) But while you can do that, it doesn't appear to accomplish anything useful - just adding the listener once would be easier than removing and re-adding every time – CertainPerformance Jan 20 '22 at 18:17
  • What is the rationale for this? It doesn't make sense to remove the listener at the start of the handler, and then attach it again at the end of the handler. Any listener can't fire while the handler is executed, hence it's just not useful to do this. Maybe you need a [debouncer](https://stackoverflow.com/a/4298672/1169519) ..? – Teemu Jan 20 '22 at 18:21
  • Ah, reading the question dozen times reveals the purpose. You can call `setSize` before/after you've attached the listener. Just like `window.addEventListener('resize', setSize); setSize();`, and remove the event attaching from the function. You still might benefit the debouncer I've linked above, `resize` event fires somewhat 60 times per second. – Teemu Jan 20 '22 at 18:39
  • @CertainPerformance It looks like OP has called the event handler when attaching the event on purpose, they want to initialize the canvas with the same code. Maybe we should re-open the question ..? – Teemu Jan 20 '22 at 18:44
  • 1
    @Teemu I don't get it, but feel free to take a shot at it – CertainPerformance Jan 20 '22 at 18:52
  • The motivation behind my question is that I'd like an elegant single function that handles both initial setup and later window resizes, rather than having to define setSize(), call it, then create an event listener that also calls it. I quickly realized after I posted this question that the reason why I must specify the `event` on `removeEventListener()` is because that is the event that `setSize()` is bound to trigger on. I thought that it was saying it would remove the event listener only when the event triggers. I have updated my question to reflect this. – Michael Moreno Jan 20 '22 at 18:55
  • @Teemu the rationale for removing the event listener at the beginning and then adding it again at the end is to handle recursive calls. If resize is triggered and setSize is recursively called, the first thing that it should do is remove the last event listener so that it won't stack onto the event listener being added at the end of the function that triggers recursion. It's useless on the initial setSize call, but crucial to subsequent recursions. – Michael Moreno Jan 20 '22 at 19:05
  • In you current code (or in the original code in the question) there's no event listeners attached at all, the execution falls to inifinite recursion from the first call. – Teemu Jan 20 '22 at 19:07
  • @Teemu Wait how is that? I have `document.addEventListener('resize', setSize)`, shouldn't that call `setSize` on window resizes? – Michael Moreno Jan 20 '22 at 19:12
  • 1
    Your edited code doesn't do it, but the original code did, the one where you called `setSize` instead of just referring it in both `addEventListener`s. – Teemu Jan 20 '22 at 19:14
  • 1
    @Teemu you mean when I had `addEventListenener('resize', setSize())` instead of `addEventListener('resize', setSize)`? Yeah that was just a mistake on my part that I missed while writing the question. – Michael Moreno Jan 20 '22 at 19:16
  • 1
    Defining the function and then using it as you propose in your "edit" part is the elegant way of doing. Anything else is an hack that in 2 years you'll stare at wondering why the heck it's done like that. – Kaiido Jan 20 '22 at 23:23

1 Answers1

2

You can do what you want, but it doesn't provide you to remove the listener in the handler. A short example would be like this:

function setSize () {
    canvas.width = window.innerWidth;
    canvas.height = window.innerHeight;
    return setSize;
}

document.addEventListener('resize', setSize());

This way you can call setSize for initialization while attaching the event. The function returns itself so that there will be a reference for addEventListener after calling setSize. Later when the event fires, the return value is ignored, because the handler will be called from the event queue.

Or, you can use an IIFE to initialize the canvas.

const setSize = (function setSize () {
    canvas.width = window.innerWidth;
    canvas.height = window.innerHeight;
    return setSize;
}());

document.addEventListener('resize', setSize);

But, at the end of the day, you shouldn't make such tricks, like Kaiido has said in the comments, "An elegant code is a code that is obvious". Just call the function for initialization. Dropping the useless return statement from the resize handler will save you a microsecond or two for the canvas updating.

Teemu
  • 22,918
  • 7
  • 53
  • 106
  • Ah! Adding parenthesis to the passed in function will immediately execute the function, and then return a reference of itself to the event listener to later call on trigger. Nice! I like this solution. – Michael Moreno Jan 20 '22 at 19:14
  • 1
    @MichaelMoreno Yes, it's sometimes used concept, though the called function is usually not the same that will be attached to the event. – Teemu Jan 20 '22 at 19:16
  • The resize event is tied to animation frames, while resizing a canvas is indeed performance consumptive that's still what one wants in general. And returning the function is just unclear. Why a function called `setSize` would return a function? Simply call the function that you assign as a handler. – Kaiido Jan 20 '22 at 23:17
  • @Kaiido That's what I said in the comments too, but OP writes: "_I want something more elegant that handles both initial setup and window resize_". I'm not sure, if this is more elegant, I'm adding another solution. – Teemu Jan 21 '22 at 05:28
  • 1
    An elegant code is a code that is obvious. Anything deviating for the obviousness of the code makes it less elegant. Sometimes reducing clutter may make the code more elegant, but using unclear patterns for the sake of golfing the code is not elegant. And once again, updating the canvas at screen refresh rate is what the vast majority of canvas content authors want. – Kaiido Jan 21 '22 at 05:30