Espaço como Tecla K
Créé le : 25 janvier 2025
Créé le : 25 janvier 2025
Seria possível fazer o site esquecer que existe a tecla espaço, mas quando eu clicar no espaço na verdade estou clicando de fato no K?
text// ==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 document.addEventListener('keydown', function(event) { 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); })();
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):
js// ==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:
jsif (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.
fiz o teste, mas ao clicar uma vez no espaço, é mesma coisa que eu tivesse clicado duas vezes.
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:
js// 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:
jsdocument.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):
js// ==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
).Fiz o teste e funcionou!! tem vezes que acontece algo chato e você já deve saber o que é, tem vezes que por algum motivo quando clicamos em espaço, ele "move para baixo'
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:
js// ==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:
jsif ( 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”.