Prevents YouTube from refreshing when reorganizing/dragging videos in a playlist
Size
5.3 KB
Version
1.0.1
Created
Mar 6, 2026
Updated
about 1 month ago
1// ==UserScript==
2// @name YouTube - Playlist Drag Without Refresh
3// @description Prevents YouTube from refreshing when reorganizing/dragging videos in a playlist
4// @version 1.0.1
5// @match https://*.youtube.com/*
6// @icon https://www.youtube.com/s/desktop/1b43e753/img/favicon_32x32.png
7// @grant GM.xmlhttpRequest
8// ==/UserScript==
9(function() {
10 'use strict';
11
12 console.log('YouTube Playlist Drag Without Refresh - Extension loaded');
13
14 // Prevent page navigation during drag operations
15 let isDragging = false;
16 let originalPushState = null;
17 let originalReplaceState = null;
18
19 function init() {
20 // Store original history methods
21 originalPushState = history.pushState;
22 originalReplaceState = history.replaceState;
23
24 // Override history.pushState to prevent navigation during drag
25 history.pushState = function(...args) {
26 if (isDragging) {
27 console.log('Blocked pushState during drag operation');
28 return;
29 }
30 return originalPushState.apply(this, args);
31 };
32
33 // Override history.replaceState to prevent navigation during drag
34 history.replaceState = function(...args) {
35 if (isDragging) {
36 console.log('Blocked replaceState during drag operation');
37 return;
38 }
39 return originalReplaceState.apply(this, args);
40 };
41
42 // Monitor for drag operations on playlist items
43 observePlaylistDragOperations();
44
45 console.log('YouTube Playlist Drag Without Refresh - Initialized');
46 }
47
48 function observePlaylistDragOperations() {
49 // Use event delegation to catch drag events on playlist items
50 document.addEventListener('dragstart', function(e) {
51 const playlistItem = e.target.closest('ytd-playlist-video-renderer');
52 if (playlistItem) {
53 isDragging = true;
54 console.log('Drag started on playlist item');
55 }
56 }, true);
57
58 document.addEventListener('dragend', function(e) {
59 const playlistItem = e.target.closest('ytd-playlist-video-renderer');
60 if (playlistItem) {
61 console.log('Drag ended on playlist item');
62 // Delay resetting to ensure all navigation attempts are blocked
63 setTimeout(() => {
64 isDragging = false;
65 console.log('Drag operation complete');
66 }, 1000);
67 }
68 }, true);
69
70 document.addEventListener('drop', function(e) {
71 const playlistContainer = e.target.closest('ytd-playlist-video-list-renderer');
72 if (playlistContainer) {
73 console.log('Drop detected in playlist');
74 // Keep blocking navigation for a bit longer after drop
75 setTimeout(() => {
76 isDragging = false;
77 console.log('Drop operation complete');
78 }, 1500);
79 }
80 }, true);
81
82 // Also monitor for mousedown on drag handles
83 document.addEventListener('mousedown', function(e) {
84 const dragHandle = e.target.closest('.playlist-drag-handle');
85 if (dragHandle) {
86 isDragging = true;
87 console.log('Drag handle grabbed');
88 }
89 }, true);
90
91 document.addEventListener('mouseup', function(e) {
92 if (isDragging) {
93 console.log('Mouse released');
94 setTimeout(() => {
95 isDragging = false;
96 console.log('Drag interaction complete');
97 }, 1000);
98 }
99 }, true);
100 }
101
102 // Intercept YouTube's navigation system
103 function interceptYouTubeNavigation() {
104 // Intercept yt-navigate events that YouTube uses for navigation
105 document.addEventListener('yt-navigate', function(e) {
106 if (isDragging) {
107 console.log('Blocked yt-navigate event during drag');
108 e.stopPropagation();
109 e.stopImmediatePropagation();
110 e.preventDefault();
111 }
112 }, true);
113
114 document.addEventListener('yt-navigate-start', function(e) {
115 if (isDragging) {
116 console.log('Blocked yt-navigate-start event during drag');
117 e.stopPropagation();
118 e.stopImmediatePropagation();
119 e.preventDefault();
120 }
121 }, true);
122
123 document.addEventListener('yt-navigate-finish', function(e) {
124 if (isDragging) {
125 console.log('Blocked yt-navigate-finish event during drag');
126 e.stopPropagation();
127 e.stopImmediatePropagation();
128 e.preventDefault();
129 }
130 }, true);
131 }
132
133 // Wait for page to be ready
134 if (document.readyState === 'loading') {
135 document.addEventListener('DOMContentLoaded', function() {
136 init();
137 interceptYouTubeNavigation();
138 });
139 } else {
140 init();
141 interceptYouTubeNavigation();
142 }
143
144 // Also handle YouTube's SPA navigation
145 document.addEventListener('yt-page-data-updated', function() {
146 console.log('YouTube page updated, re-initializing if needed');
147 });
148
149})();