Use 'once' instead of 'on' to avoid EventListener leaks