Espaço como Tecla K
Создано: 25 января 2025 г.
Создано: 25 января 2025 г.
Transforming key events
Interestingly enough, the user is looking to intercept the Space key press, not triggering the default pause function, and instead treating it as if the 'K' key was pressed.
Intercepting and adjusting
I’m working through intercepting the Space key press by applying preventDefault and stopPropagation, then invoking handlePlayPause to simulate a 'K' key press function, achieving the desired outcome.
Disabling default space behavior
I'm mapping out how to disable the default Space key function while ensuring the 'K' key function is triggered instead, avoiding potential double triggers with a fake dispatch method.
Thought about remapear tecla espaço para K for 6 seconds
Sim, é possível fazer isso interceptando o evento de pressionar a barra de espaço e chamando diretamente a mesma função que seria chamada ao pressionar a tecla K (no caso, a handlePlayPause
). Basicamente, você:
K
.Para isso, basta adicionar um listener de keydown
para a barra de espaço e dentro dele disparar o handlePlayPause
. Segue um exemplo integrado ao seu script (note a parte adicionada para a barra de espaço):
// ==UserScript== // @name YouTube Pause Fadeout/Fadein // @namespace https://exemplo-de-namespace // @version 1.0 // @description Aplica fade out/in de áudio ao pausar/reproduzir o vídeo no YouTube // @match https://www.youtube.com/* // @grant none // ==/UserScript== (function() { 'use strict'; // Ajuste estes valores conforme sua preferência const FADE_DURATION_MS = 500; // Duração total do fade em milissegundos const FADE_STEPS = 20; // Quantos "degraus" de volume const DEFAULT_VOLUME = 1.0; // Volume padrão para restaurar const DEBOUNCE_TIME = 200; // Tempo mínimo entre ações de play/pause const intervalId = setInterval(() => { const video = document.querySelector('video'); if (video) { clearInterval(intervalId); let originalVolume = video.volume; let fadeInterval = null; let lastActionTime = 0; let isHandlingPlayPause = false; function fadeOutAndPause() { if (video.paused) return; // Limpa qualquer fade que possa estar em andamento if (fadeInterval) clearInterval(fadeInterval); originalVolume = video.volume; let currentVolume = video.volume; const volumeStep = currentVolume / FADE_STEPS; const stepInterval = FADE_DURATION_MS / FADE_STEPS; fadeInterval = setInterval(() => { currentVolume -= volumeStep; if (currentVolume <= 0) { currentVolume = 0; clearInterval(fadeInterval); fadeInterval = null; isHandlingPlayPause = true; video.pause(); setTimeout(() => { isHandlingPlayPause = false; }, 50); } video.volume = currentVolume; }, stepInterval); } function fadeIn() { // Limpa qualquer fade que possa estar em andamento if (fadeInterval) clearInterval(fadeInterval); let currentVolume = 0; video.volume = currentVolume; const volumeStep = originalVolume / FADE_STEPS; const stepInterval = FADE_DURATION_MS / FADE_STEPS; fadeInterval = setInterval(() => { currentVolume += volumeStep; if (currentVolume >= originalVolume) { currentVolume = originalVolume; clearInterval(fadeInterval); fadeInterval = null; } video.volume = currentVolume; }, stepInterval); } function handlePlayPause(event) { const now = Date.now(); if (now - lastActionTime < DEBOUNCE_TIME) { if(event) { event.stopPropagation(); event.stopImmediatePropagation(); event.preventDefault(); } return; } lastActionTime = now; // Sempre previne o comportamento padrão if(event) { event.stopPropagation(); event.stopImmediatePropagation(); event.preventDefault(); } if (!video.paused) { fadeOutAndPause(); } else { video.volume = 0; // Começa mudo isHandlingPlayPause = true; video.play(); setTimeout(() => { isHandlingPlayPause = false; }, 50); fadeIn(); } } // Interceptando o clique no vídeo video.addEventListener('click', handlePlayPause, true); // Interceptando a tecla K e a barra de espaço document.addEventListener('keydown', function(event) { // Para "esquecer" a barra de espaço e tratá-la como se fosse K if (event.code === 'Space' || event.key === ' ') { handlePlayPause(event); } // Mantemos o suporte à tecla K else if (event.key.toLowerCase() === 'k') { handlePlayPause(event); } }, true); // Observando mudanças no estado de reprodução video.addEventListener('play', function() { if (!isHandlingPlayPause) { video.volume = 0; fadeIn(); } }); video.addEventListener('pause', function() { if (!isHandlingPlayPause) { fadeOutAndPause(); } }); } }, 1000); })();
O trecho:
if (event.code === 'Space' || event.key === ' ') { handlePlayPause(event); }
impede o comportamento padrão do espaço (pois chamamos event.preventDefault()
) e chama a mesma função que o código chama ao pressionar a tecla K.
Isso faz com que, ao pressionar barra de espaço, você obtenha exatamente o mesmo resultado do que pressionar K no player do YouTube.
Dessa forma, o “site esquece” o que a barra de espaço faz normalmente e passa a tratá-la exatamente como se fosse a tecla K.
Identifying the cause
Hm, that's interesting. Pressing space triggers two toggles, likely due to YouTube's script capturing the event or the default action not being properly canceled.
Adjusting event handling
Setting the script to listen in the capturing phase with event.stopPropagation()
and event.preventDefault()
should effectively prevent YouTube's script from responding to the space event.
Adjusting to event focus
Pressing space might toggle twice due to the video being focused, triggering a standard pause/play action. Ignoring if the active element is not the body or monitoring specific key events could help.
Intercepting space event
Using capture: true
, the script intercepts the space event, cancels the default, and stops propagation to prevent YouTube's script from responding to it.
Preventing trigger
I'm thinking through using event.stopPropagation()
and event.preventDefault()
to block YouTube's space event trigger. Setting isHandlingPlayPause = true
might be crucial too.
Evaluating solutions
Thinking about different methods like the capturing approach, checking e.repeat
, and setting video.tabIndex = -1
to stop YouTube's space event trigger and adjust playback.
Adjusting event handling
Implementing event.preventDefault()
and event.stopPropagation()
in the capturing phase for both keydown
and keyup
can potentially prevent YouTube's space event trigger, mitigating double-toggling.
Thought about interactive event handling for 12 seconds
O sintoma de "pressionar espaço uma vez e parecer que o player foi pausado/reproduzido duas vezes" acontece porque, além do seu script, o YouTube (ou o próprio navegador) também está ouvindo a barra de espaço e executando outra ação de pausa/play.
Para resolver, precisamos interceptar completamente o espaço antes que o YouTube faça a ação dele. Mesmo que você use event.preventDefault()
, stopPropagation()
, etc., pode ser que o YouTube esteja ouvindo eventos em uma fase diferente (por exemplo, capturing vs bubbling) ou usando outro tipo de evento (keydown
, keypress
, keyup
).
A forma mais confiável é:
addEventListener
como true
).keydown
, mas também keyup
e/ou keypress
, bloqueando-os totalmente para que o YouTube não os capture depois.Assim:
// Intercepta e bloqueia completamente a barra de espaço function blockSpaceEvent(event) { if ( event.code === 'Space' || // Padrão moderno (ex: "Space") event.key === ' ' // Alguns navegadores podem usar ' ' ) { event.preventDefault(); event.stopPropagation(); event.stopImmediatePropagation(); return true; // Apenas um "marcador" se quisermos checar depois } return false; }
Depois, no seu script principal, chame:
document.addEventListener('keydown', function(e) { if (blockSpaceEvent(e)) { // Se for espaço, chamamos nossa função de play/pause handlePlayPause(e); } }, true); document.addEventListener('keypress', function(e) { // Bloqueia totalmente se for espaço, mas sem chamar handlePlayPause aqui blockSpaceEvent(e); }, true); document.addEventListener('keyup', function(e) { // Bloqueia totalmente se for espaço, mas sem chamar handlePlayPause blockSpaceEvent(e); }, true);
document.addEventListener(..., true)
: registra o listener em modo capturing, ou seja, seu código captura o evento antes de chegar em qualquer outro handler que o YouTube registrou.keydown
, keypress
, keyup
), chamamos blockSpaceEvent(e)
.
event.preventDefault()
, stopPropagation()
, stopImmediatePropagation()
. Isso garante que o evento não “escorra” para o YouTube.handlePlayPause()
no keydown
, para não disparar repetidamente no keypress
ou keyup
.Alguns sites (ou navegadores) podem pausar/reproduzir o vídeo tanto no keydown
como no keypress
(ou checar no keyup
). Interceptando em todos, garantimos que não passe nada “duas vezes”.
Se a sua intenção for apenas bloquear espaço sem usar espaço como “atalho” para play/pause, basta chamar o blockSpaceEvent(e)
nos três eventos. A tecla não chega ao player, e o espaço não faz nada (ou seja, nenhum duplo clique).
Abaixo vai um exemplo integrando essas correções ao seu script original (o essencial para evitar o “duplo toggle” é a parte do capturing e a interceptação de todos os tipos de eventos de espaço):
// ==UserScript== // @name YouTube Pause Fadeout/Fadein // @namespace https://exemplo-de-namespace // @version 1.0 // @description Aplica fade out/in de áudio ao pausar/reproduzir o vídeo no YouTube // @match https://www.youtube.com/* // @grant none // ==/UserScript== (function() { 'use strict'; // Ajuste estes valores conforme sua preferência const FADE_DURATION_MS = 500; // Duração total do fade em milissegundos const FADE_STEPS = 20; // Quantos "degraus" de volume const DEFAULT_VOLUME = 1.0; // Volume padrão para restaurar const DEBOUNCE_TIME = 200; // Tempo mínimo entre ações de play/pause // --------------------------------------- // FUNÇÃO auxiliar para bloquear Espaço // --------------------------------------- function blockSpaceEvent(event) { if (event.code === 'Space' || event.key === ' ') { event.preventDefault(); event.stopPropagation(); event.stopImmediatePropagation(); return true; } return false; } const intervalId = setInterval(() => { const video = document.querySelector('video'); if (video) { clearInterval(intervalId); let originalVolume = video.volume; let fadeInterval = null; let lastActionTime = 0; let isHandlingPlayPause = false; function fadeOutAndPause() { if (video.paused) return; if (fadeInterval) clearInterval(fadeInterval); originalVolume = video.volume; let currentVolume = video.volume; const volumeStep = currentVolume / FADE_STEPS; const stepInterval = FADE_DURATION_MS / FADE_STEPS; fadeInterval = setInterval(() => { currentVolume -= volumeStep; if (currentVolume <= 0) { currentVolume = 0; clearInterval(fadeInterval); fadeInterval = null; isHandlingPlayPause = true; video.pause(); setTimeout(() => { isHandlingPlayPause = false; }, 50); } video.volume = currentVolume; }, stepInterval); } function fadeIn() { if (fadeInterval) clearInterval(fadeInterval); let currentVolume = 0; video.volume = currentVolume; const volumeStep = originalVolume / FADE_STEPS; const stepInterval = FADE_DURATION_MS / FADE_STEPS; fadeInterval = setInterval(() => { currentVolume += volumeStep; if (currentVolume >= originalVolume) { currentVolume = originalVolume; clearInterval(fadeInterval); fadeInterval = null; } video.volume = currentVolume; }, stepInterval); } function handlePlayPause(event) { const now = Date.now(); if (now - lastActionTime < DEBOUNCE_TIME) { if(event) { event.preventDefault(); event.stopPropagation(); event.stopImmediatePropagation(); } return; } lastActionTime = now; if(event) { event.preventDefault(); event.stopPropagation(); event.stopImmediatePropagation(); } if (!video.paused) { fadeOutAndPause(); } else { video.volume = 0; isHandlingPlayPause = true; video.play(); setTimeout(() => { isHandlingPlayPause = false; }, 50); fadeIn(); } } // Interceptando o clique no vídeo video.addEventListener('click', handlePlayPause, true); // Interceptando a tecla K document.addEventListener('keydown', function(event) { // Se a tecla for 'k', chamamos handlePlayPause if (event.key.toLowerCase() === 'k') { handlePlayPause(event); } }, true); // --------------------------------------- // Interceptando completamente a barra de espaço // --------------------------------------- document.addEventListener('keydown', function(event) { // Se for espaço, bloqueamos e chamamos handlePlayPause if (blockSpaceEvent(event)) { handlePlayPause(event); } }, true); // Bloqueia espaço no keypress e keyup (sem chamar handlePlayPause) document.addEventListener('keypress', blockSpaceEvent, true); document.addEventListener('keyup', blockSpaceEvent, true); // Observando mudanças no estado de reprodução video.addEventListener('play', function() { if (!isHandlingPlayPause) { video.volume = 0; fadeIn(); } }); video.addEventListener('pause', function() { if (!isHandlingPlayPause) { fadeOutAndPause(); } }); } }, 1000); })();
Desse modo, quando você pressionar barra de espaço:
preventDefault
+ stopPropagation
+ stopImmediatePropagation
).keydown
).Ensuring no scrolling with event.preventDefault()
Trying out different methods. I’m thinking about keypress or event capture phases to ensure no scrolling. This approach helps refine our strategy.
Evaluating responses
I’m looking at refining event handling or adjusting body styles to address the user's complaint that pressing space occasionally causes the page to scroll down unintentionally.
Tweaking the approach
I’m thinking about adding logic to avoid triggering handlePlayPause when pressing space in an input or textarea, thus preventing potentially unwanted page scrolling.
Evaluating blocking methods
OK, let me see: I'm thinking about blocking space key events during keydown, keypress, and keyup to stop page scrolling, focusing on capturing the event to prevent default behavior.
Adjusting my approach
I'm incorporating event listeners to capture keydown, keypress, and keyup events, ensuring preventDefault is applied to block the default scroll behavior. This strategy should effectively control scrolling.
Thought about spacebar behavior fix for 8 seconds
Esse “pular para baixo” acontece porque, por padrão, quando não há foco em um campo de texto ou similar, a barra de espaço costuma rolar a página. Mesmo usando preventDefault()
e stopPropagation()
, em alguns cenários o navegador ainda pode interpretar o keydown
/keypress
/keyup
e fazer o scroll. A boa notícia é que quase sempre dá para bloquear completamente com alguns cuidados:
Escutar os eventos em modo capturing (o terceiro parâmetro true
do addEventListener
):
Isso garante que seu código capture o evento antes de chegar no YouTube ou em qualquer outro elemento da página.
Interceptar keydown
, keypress
e keyup
, todos eles, e chamar preventDefault()
, stopPropagation()
, stopImmediatePropagation()
no caso de ser a barra de espaço.
Usar passive: false
ao adicionar o listener, para que o navegador não ignore o preventDefault()
em certos cenários de rolagem.
No seu script, tente algo assim (o essencial está no bloco onde bloqueamos completamente a tecla espaço). Observe o uso de { capture: true, passive: false }
nos listeners:
// ==UserScript== // @name YouTube Pause Fadeout/Fadein // @namespace https://exemplo-de-namespace // @version 1.0 // @description Aplica fade out/in de áudio ao pausar/reproduzir o vídeo no YouTube // @match https://www.youtube.com/* // @grant none // ==/UserScript== (function() { 'use strict'; const FADE_DURATION_MS = 500; // Duração do fade em ms const FADE_STEPS = 20; // "Degraus" de volume const DEBOUNCE_TIME = 200; // Intervalo mínimo entre play/pause // Bloqueia eventos de espaço e retorna true se for espaço function blockSpaceEvent(event) { // Verifica se a tecla é espaço if (event.code === 'Space' || event.key === ' ') { event.preventDefault(); event.stopPropagation(); event.stopImmediatePropagation(); return true; } return false; } const intervalId = setInterval(() => { const video = document.querySelector('video'); if (video) { clearInterval(intervalId); let originalVolume = video.volume; let fadeInterval = null; let lastActionTime = 0; let isHandlingPlayPause = false; function fadeOutAndPause() { if (video.paused) return; if (fadeInterval) clearInterval(fadeInterval); originalVolume = video.volume; let currentVolume = video.volume; const volumeStep = currentVolume / FADE_STEPS; const stepInterval = FADE_DURATION_MS / FADE_STEPS; fadeInterval = setInterval(() => { currentVolume -= volumeStep; if (currentVolume <= 0) { currentVolume = 0; clearInterval(fadeInterval); fadeInterval = null; isHandlingPlayPause = true; video.pause(); setTimeout(() => { isHandlingPlayPause = false; }, 50); } video.volume = currentVolume; }, stepInterval); } function fadeIn() { if (fadeInterval) clearInterval(fadeInterval); let currentVolume = 0; video.volume = currentVolume; const volumeStep = originalVolume / FADE_STEPS; const stepInterval = FADE_DURATION_MS / FADE_STEPS; fadeInterval = setInterval(() => { currentVolume += volumeStep; if (currentVolume >= originalVolume) { currentVolume = originalVolume; clearInterval(fadeInterval); fadeInterval = null; } video.volume = currentVolume; }, stepInterval); } function handlePlayPause(event) { const now = Date.now(); if (now - lastActionTime < DEBOUNCE_TIME) { if(event) { event.preventDefault(); event.stopPropagation(); event.stopImmediatePropagation(); } return; } lastActionTime = now; if(event) { event.preventDefault(); event.stopPropagation(); event.stopImmediatePropagation(); } if (!video.paused) { fadeOutAndPause(); } else { video.volume = 0; isHandlingPlayPause = true; video.play(); setTimeout(() => { isHandlingPlayPause = false; }, 50); fadeIn(); } } // Clique no vídeo video.addEventListener('click', handlePlayPause, true); // Tecla K -> play/pause document.addEventListener('keydown', function(e) { if (e.key.toLowerCase() === 'k') { handlePlayPause(e); } }, true); // =============== BLOQUEIO COMPLETO DO ESPAÇO =============== // keydown + capturing + passive=false => impede rolagem e bloqueia no início document.addEventListener('keydown', function(e) { if (blockSpaceEvent(e)) { // Se for espaço, executa play/pause (só aqui para não repetir em keypress) handlePlayPause(e); } }, { capture: true, passive: false }); // keypress e keyup -> só bloqueia, sem chamar play/pause document.addEventListener('keypress', function(e) { blockSpaceEvent(e); }, { capture: true, passive: false }); document.addEventListener('keyup', function(e) { blockSpaceEvent(e); }, { capture: true, passive: false }); // =========================================================== // Ao dar play manualmente video.addEventListener('play', function() { if (!isHandlingPlayPause) { video.volume = 0; fadeIn(); } }); // Ao dar pause manualmente video.addEventListener('pause', function() { if (!isHandlingPlayPause) { fadeOutAndPause(); } }); } }, 1000); })();
{ capture: true, passive: false }
:
capture: true
faz o script pegar o evento antes dos handlers do YouTube ou do próprio navegador.passive: false
permite que o preventDefault()
realmente bloqueie a rolagem.Se ainda ocorrer scroll em algum caso muito específico (por exemplo, se o foco estiver em um campo de texto e você pressionar espaço), o navegador pode interpretar diferente. Para “desabilitar” totalmente, é preciso garantir que o evento nunca chegue ao navegador sem o preventDefault()
.
Se você precisar digitar espaço em algum momento (por exemplo, num campo de busca), esse script também bloqueará, pois estamos interceptando espaço globalmente. Aí precisaria de uma checagem adicional do event.target
para permitir espaço em <input>
, <textarea>
etc. Por exemplo:
if ( event.target.tagName === 'INPUT' || event.target.tagName === 'TEXTAREA' || event.target.isContentEditable ) { // Deixa passar o espaço para poder digitar normalmente return false; }
antes de chamar event.preventDefault()
, etc.
Mas, no geral, com esse bloqueio em todas as fases e sendo “não-passivo”, o navegador costuma parar de rolar a página ao pressionar espaço, resolvendo o problema de “move para baixo”.