この記事の概要
- Enhancer for YouTubeがCSSを追加する
- そのCSSの影響でVivaldiのPiPボタンが
z-indexの競合(stacking context)で背後に隠れてしまう - UserScript で PiP ボタンの親
divのz-indexを変更することで解決できる
1. はじめに
こんにちは、ピーラップ(Prrapp)です。
私は普段ブラウザとして Vivaldi を使っています。
YouTubeを見る際は拡張機能「Enhancer for YouTube」を利用しているのですが、次の問題に気づきました。
YouTubeだけ Vivaldi ネイティブの PiP(Picture-in-Picture)が使えない。
原因を調べたところ、拡張機能によって追加される CSS の影響で
PiP ボタンがプレイヤーの背後に隠れてしまいクリックできない状態になっていました。
そこで今回は、この問題を UserScript で解決する方法 を紹介します。
2. この記事の概要
2-1. 対象読者
- Vivaldi を使用している
- Enhancer for YouTube を使っている
- PiP が表示されない問題に困っている
2-2. 症状例
動画プレイヤーにマウスカーソルを重ねてもPiPボタンが表示されません。(Snipping Toolではマウスカーソルがキャプチャされないため表示されていません)

(引用元:フレデリック「オドループ」Music Video | Frederic "oddloop")
今回作成したUserScriptを使用して正常にした場合、このようになります。

(引用元:フレデリック「オドループ」Music Video | Frederic "oddloop")
(シークバーが緑色になっているのはEnhancer for YouTubeの機能です)
2-3. 技術ポイント
今回の問題では次の技術要素が関係しています。
- CSS
z-index - Shadow DOM
- ブラウザ拡張によるUI干渉
- UserScriptによるDOM操作
3. 環境
| 項目 | バージョン |
|---|---|
| ブラウザ | Vivaldi 7.8.3925.76 (64-bit) |
| カスタムバージョン | 144.0.7559.238 |
| OS | Windows 11 25H2 (Build 26200.7922) |
| Enhancer for YouTube | 3.0.16 |
| UserScriptマネージャー | Tampermonkey 5.4.1 |
4. 原因
4-1. 症状
Vivaldi Forumで指摘されていますが、VivaldiではPiPボタンに z-index: 10 が設定されています。
しかし、Enhancer for YouTube はプレイヤー要素に z-index: 301 を追加します。
その結果、プレイヤーがPiPボタンの上に表示されてしまい、クリックできなくなります。
それが分かったので、自分で調べて実装を考えてみます。
イメージとして断面図で表すと、次のような状態になります。
見る方向
↓
[プレイヤー (z-index: 301)]
[PiPボタン (z-index: 10)]
プレイヤー要素の z-index が高いため、
PiPボタンがプレイヤーの背後に隠れてしまいます。
4-2. ネイティブPiPボタンの構造
VivaldiのPiPボタン自体はこんな感じの構造になっていました。
/
└─ div(クラスなし)
└─ #shadow-root (open)
├─ link(Vivaldi内部拡張へのリンク)
└─ div(クラス名:vivaldi-picture-in-picture-container)
└─ input
4-3. 検証
まず、PiPボタン側の z-index を 2147483647 (z-indexの最大値)に変更してみました。
しかし、これだけでは表示されませんでした。
原因は、z-index の競合が 親要素のレイヤー構造 に依存していたためです。
そこで、Shadow DOMの外側にある親要素(div)の z-index を
2147483647に変更すると正常に表示されることを確認できました。
5. 解決策
PiPボタンは Shadow DOM 内にありますが、
.vivaldi-picture-in-picture-container から親要素を取得することで
外側の div にアクセスできます。(外側のdivはクラス名とidがないので直接取得するのは難しいです)
そこで以下の方針で解決します。
-
.vivaldi-picture-in-picture-containerを取得 - Shadow DOM の外側の親
divを取得 - その
z-indexを2147483647に変更
この処理をUserScriptで実装します。
6. 実装
長いので折りたたみにしています
// ==UserScript==
// @name VivaldiのPiPのEnhancer for YouTube使用時の不具合を解決する
// @namespace https://qiita.com/Prrapp
// @version 1.11.0
// @author Prrapp
// @description VivaldiのPiPのEnhancer for YouTube使用時の不具合を解決します。
// @match https://www.youtube.com/*
// @grant none
// @license MIT
// ==/UserScript==
(function () {
'use strict';
let cachedHost = null;
const shadowHosts = new Set();
function scanShadowHosts() {
const elements = document.querySelectorAll('*');
for (const el of elements) {
if (el.shadowRoot) shadowHosts.add(el);
}
}
function findPipHost() {
for (const host of shadowHosts) {
const root = host.shadowRoot;
if (!root) continue;
const pip = root.querySelector('.vivaldi-picture-in-picture-container');
if (!pip) continue;
const nodeRoot = pip.getRootNode();
if (!nodeRoot || !nodeRoot.host) continue;
return nodeRoot.host;
}
return null;
}
function applyZIndex() {
const host = findPipHost();
if (!host) return;
const current = getComputedStyle(host).zIndex;
if (cachedHost === host && current === '2147483647') return;
host.style.setProperty('z-index', '2147483647', 'important');
cachedHost = host;
}
scanShadowHosts();
const observer = new MutationObserver(() => {
scanShadowHosts();
applyZIndex();
});
observer.observe(document.documentElement, {
childList: true,
subtree: true
});
setInterval(() => {
if (!cachedHost || !cachedHost.isConnected) {
scanShadowHosts();
}
applyZIndex();
}, 800);
const reset = () => {
cachedHost = null;
shadowHosts.clear();
scanShadowHosts();
};
document.addEventListener('yt-navigate-finish', reset);
document.addEventListener('yt-page-data-updated', reset);
})();
7. 使い方
7-1. Greasyforkを利用した方法(推奨)
7-2. 手動インストール(非推奨)
- Tampermonkeyをインストールする
- 5-2の実装したものをコピーする
- 右上のTampermonkeyの拡張機能アイコンから「新規スクリプトを追加」
- 開いたページのコードを削除してペースト
- Ctrl(Cmd) + S(保存)
8. まとめ
Enhancer for YouTube を使用すると
Vivaldi のネイティブ PiP が表示されない問題が発生する場合があります。
原因は、拡張機能によって追加された CSS により
PiPボタンがプレイヤーの背後に隠れてしまうことでした。
この記事では以下の方法で解決しました。
- UserScript を作成
- PiPボタンの親
divのz-indexを変更 - Tampermonkey で実行
このように、ブラウザ拡張機能による UI の競合は
UserScript で DOM を調整することで回避できる場合があります。
拡張機能を自作するよりも
手軽に試せる解決方法になるケースもあります。
同じ問題で困っている方の参考になれば幸いです。
Ex. Before/After
Ex-1. Before
Ex-2. After

(Ex-1,Ex-2ともに引用元はフレデリック「オドループ」Music Video | Frederic "oddloop"です)