darkmode
by
nanikamado
2018-06-19 [2018/06/19 21:24:26]
サイトの背景色を黒にし、文字色を明るくします。詳しい説明はソースコード内の先頭のコメントを見てください。
@@ -8,111 +8,91 @@
* @javascript_url
*/
-//http://let.hatelabo.jp/nanikamado/let/hLHU5aii3YU5 の改良版です。
-// 実行する度に以下が切り替わります。
-// 0 -> 1 -> 2 -> 0 -> 1 -> 2 -> 0...
-// | 0 : - 背景の色を黒にします。ただし、背景が透明の場合は変更しません。
-// | - 文字色の彩度を最大にします。
-// | - 訪問したことのあるリンクの色を紫にします。
-// | - 画像の色は変わりません。文字や背景に画像が使われている場合、見づらくなるかもしれません。
-// | - 稀に色を変えられないこともあります。仕様です。
-// |
-// | 1 : - 全ての要素の背景が黒になります。
-// | - はじめに色が変わらなかったテキストでも色が変わる可能性があります。
-// | - 訪問したことのあるリンクの色は、そうでないリンクの色と同じになります。
-// | - 画像の色は変わりません。
-// |
-// | 2 : - もとに戻ります。
-// v
+// 背景の色を黒にします。ただし、背景が透明の場合は変更しません。
+// 文字色の彩度を最大にします。
+// 訪問したことのあるリンクの色を紫にします。
+// 稀に色を変えられないこともあります。仕様です。
+// 画像の色は変わりません。
+// 一度実行した状態でもう一度実行すると、もとに戻ります。
+//http://let.hatelabo.jp/nanikamado/let/hLHU5aii3YU5 の改良版です。
(() => {
'use strict';
- const set_color = (nodes, background) => {
- const imp = background === 'minimum' ? undefined : 'important';
+ const set_color = (nodes) => {
nodes.forEach((node) => {
- if (typeof node.getAttribute !== "function") return;
+ if (!node.style) return;
if (node.getAttribute('dark_before_style') === null) node.setAttribute('dark_before_style', node.style.cssText);
const computed_style = window.getComputedStyle(node);
if (!computed_style.color) return;
- const [r, g, b, ] = computed_style.color.match(/\d+/g).slice(0, 3).map(Number);
+ const [r, g, b] = computed_style.color.match(/\d+/g).slice(0, 3).map(Number);
const s = 255 - Math.max(r, g, b);
- node.style.setProperty('color', `rgb(${r+s},${g+s},${b+s})`, imp);
- if (background === 'minimum' && computed_style.backgroundColor.indexOf('a') !== -1) {
- if (computed_style.backgroundColor.indexOf('rgba(0, 0, 0, 0)') === -1)
- node.style.setProperty('background-color', 'rgba(0, 0, 0,' +
- computed_style.backgroundColor.replace(/[^,]*,[^,]+,[^,]+,[^,\d]*(\d?.?\d+)/, '$1'), 'important');
- } else {
+ if (node.tagName === 'A')
+ node.style.color = `rgb(${r+s},${g+s},${b+s})`;
+ else
+ node.style.setProperty('color', `rgb(${r+s},${g+s},${b+s})`, 'important');
+
+ if (computed_style.backgroundColor.indexOf('a') === -1)
node.style.setProperty('background-color', '#000', 'important');
- }
+ else if (computed_style.backgroundColor.indexOf('rgba(0, 0, 0, 0)') === -1)
+ node.style.setProperty('background-color', 'rgba(0, 0, 0,' +
+ computed_style.backgroundColor.replace(/[^,]*,[^,]+,[^,]+,[^,\d]*(\d?.?\d+)/, '$1'), 'important');
+
if (computed_style.backgroundImage.indexOf('gradient') !== -1)
node.style.backgroundImage = computed_style.backgroundImage
.replace(/[^,]*gradient\((?:[^\(\)]*\((?:[^\(\)]*\([^\(\)]*\))*[^\(\)]*[^\(\)]*\))*[^\(\)]*\),?/, '') || 'none';
-
});
};
- const set_color_all = (background) => {
- set_color(document.body.querySelectorAll(':not(style):not(script)'), background);
- set_color([document.body, document.body.parentNode], background);
+ const set_color_all = () => {
+ set_color(document.body.querySelectorAll('*'));
+ set_color([document.body, document.body.parentNode]);
document.body.querySelectorAll('iframe[src]').forEach(iframe => {
if (!iframe.src) return;
if ((new URL(iframe.src, location.href)).origin === location.origin || iframe.src === 'about:blank') {
- set_color(iframe.contentDocument.body.querySelectorAll('*'), background);
+ set_color(iframe.contentDocument.body.querySelectorAll('*'));
let if_dc_b = iframe.contentDocument.body || console.error(iframe);
if_dc_b.style.backgroundColor = '#000';
}
});
document.body.style.backgroundColor = '#000';
};
-
- switch (document.body.getAttribute('dark_mode_state') || '0') {
- case '0':
- let observer = new MutationObserver((maa) => {
- let imp;
- switch (document.body.getAttribute('dark_mode_state')) {
- case '0':
+ const main = () => {
+ switch (document.body.getAttribute('dark_mode_state') || '0') {
+ case '0':
+ let observer = new MutationObserver((maa) => {
+ if (document.body.getAttribute('dark_mode_state') === '0') {
observer.disconnect();
- return;
- case '1':
- imp = 'minimum';
- break;
- case '2':
- imp = 'all';
- break;
- }
- maa.forEach(ma => {
- set_color(ma.addedNodes, imp);
- ma.addedNodes.forEach(added_node => {
- if (added_node.nodeName === '#text') return;
- set_color(added_node.querySelectorAll('*'), imp);
+ }
+ maa.forEach(ma => {
+ set_color(ma.addedNodes);
+ ma.addedNodes.forEach(added_node => {
+ if (added_node.nodeName === '#text') return;
+ set_color(added_node.querySelectorAll('*'));
+ });
});
+ console.log('reseted');
});
- console.log('reseted');
- });
- let resize_timer,
- resize_listener;
- window.addEventListener('resize', resize_listener = () => {
- console.log('resizing');
- if (resize_timer) clearTimeout(resize_timer);
- resize_timer = setTimeout(() => {
- switch (document.body.getAttribute('dark_mode_state')) {
- case '0':
- window.removeEventListener('resize', resize_listener);
- break;
- case '1':
- set_color_all('minimum');
- break;
- case '2':
- set_color_all('all');
- break;
- }
- }, 500);
- });
+ let resize_timer,
+ resize_listener = () => {
+ console.log('resizing');
+ if (resize_timer) clearTimeout(resize_timer);
+ resize_timer = setTimeout(() => {
+ switch (document.body.getAttribute('dark_mode_state')) {
+ case '0':
+ window.removeEventListener('resize', resize_listener);
+ break;
+ case '1':
+ set_color_all();
+ break;
+ }
+ }, 500);
+ };
+ window.addEventListener('resize', resize_listener);
- let dark_style = document.createElement('style');
- dark_style.textContent = `a:visited {
+ let dark_style = document.createElement('style');
+ dark_style.textContent = `a:visited {
color: #b553ff!important
}
*{
@@ -134,34 +114,35 @@
border-radius: .4rem;
background: #ddd;
}`;
- dark_style.className = 'dark_visited_style';
- document.body.appendChild(dark_style);
+ dark_style.className = 'dark_visited_style';
+ document.body.appendChild(dark_style);
+
+ document.body.setAttribute('dark_mode_state', '1');
+ set_color_all();
+ observer.observe(document.body, {
+ childList: true,
+ subtree: true
+ });
+ console.log('0');
+ break;
+
+ case '1':
+ document.body.setAttribute('dark_mode_state', '0');
+ document.querySelectorAll('*').forEach(node => {
+ if (!node.style) return;
+ const style = node.getAttribute('dark_before_style');
+ if (style === null) return;
+ node.style.cssText = style;
+ });
+ document.body.getElementsByClassName('dark_visited_style')[0].remove();
+ console.log('1');
+ break;
+ }
+ };
- document.body.setAttribute('dark_mode_state', '1');
- set_color_all('minimum');
- observer.observe(document.body, {
- childList: true,
- subtree: true
- });
- console.log('0');
- break;
-
- case '1':
- document.body.setAttribute('dark_mode_state', '2');
- set_color_all('all');
- console.log('1');
- break;
-
- case '2':
- document.body.setAttribute('dark_mode_state', '0');
- document.querySelectorAll('*').forEach(node => {
- if (!node.style) return;
- const style = node.getAttribute('dark_before_style');
- if (style === null) return;
- node.style.cssText = style;
- });
- document.body.getElementsByClassName('dark_visited_style')[0].remove();
- console.log('2');
- break;
+ if (document.readyState == 'loading') {
+ document.addEventListener('DOMContentLoaded', main);
+ } else {
+ main()
}
})();
/*
* @title darkmode
* @description サイトの背景色を黒にし、文字色を明るくします。詳しい説明はソースコード内の先頭のコメントを見てください。
* @include http://*
* @include https://*
* @contributor noromanba http://let.hatelabo.jp/noromanba/let/hJmc5cn7v_h5
* @license MIT license https://opensource.org/licenses/MIT
* @javascript_url
*/
// 背景の色を黒にします。ただし、背景が透明の場合は変更しません。
// 文字色の彩度を最大にします。
// 訪問したことのあるリンクの色を紫にします。
// 稀に色を変えられないこともあります。仕様です。
// 画像の色は変わりません。
// 一度実行した状態でもう一度実行すると、もとに戻ります。
//http://let.hatelabo.jp/nanikamado/let/hLHU5aii3YU5 の改良版です。
(() => {
'use strict';
const set_color = (nodes) => {
nodes.forEach((node) => {
if (!node.style) return;
if (node.getAttribute('dark_before_style') === null) node.setAttribute('dark_before_style', node.style.cssText);
const computed_style = window.getComputedStyle(node);
if (!computed_style.color) return;
const [r, g, b] = computed_style.color.match(/\d+/g).slice(0, 3).map(Number);
const s = 255 - Math.max(r, g, b);
if (node.tagName === 'A')
node.style.color = `rgb(${r+s},${g+s},${b+s})`;
else
node.style.setProperty('color', `rgb(${r+s},${g+s},${b+s})`, 'important');
if (computed_style.backgroundColor.indexOf('a') === -1)
node.style.setProperty('background-color', '#000', 'important');
else if (computed_style.backgroundColor.indexOf('rgba(0, 0, 0, 0)') === -1)
node.style.setProperty('background-color', 'rgba(0, 0, 0,' +
computed_style.backgroundColor.replace(/[^,]*,[^,]+,[^,]+,[^,\d]*(\d?.?\d+)/, '$1'), 'important');
if (computed_style.backgroundImage.indexOf('gradient') !== -1)
node.style.backgroundImage = computed_style.backgroundImage
.replace(/[^,]*gradient\((?:[^\(\)]*\((?:[^\(\)]*\([^\(\)]*\))*[^\(\)]*[^\(\)]*\))*[^\(\)]*\),?/, '') || 'none';
});
};
const set_color_all = () => {
set_color(document.body.querySelectorAll('*'));
set_color([document.body, document.body.parentNode]);
document.body.querySelectorAll('iframe[src]').forEach(iframe => {
if (!iframe.src) return;
if ((new URL(iframe.src, location.href)).origin === location.origin || iframe.src === 'about:blank') {
set_color(iframe.contentDocument.body.querySelectorAll('*'));
let if_dc_b = iframe.contentDocument.body || console.error(iframe);
if_dc_b.style.backgroundColor = '#000';
}
});
document.body.style.backgroundColor = '#000';
};
const main = () => {
switch (document.body.getAttribute('dark_mode_state') || '0') {
case '0':
let observer = new MutationObserver((maa) => {
if (document.body.getAttribute('dark_mode_state') === '0') {
observer.disconnect();
}
maa.forEach(ma => {
set_color(ma.addedNodes);
ma.addedNodes.forEach(added_node => {
if (added_node.nodeName === '#text') return;
set_color(added_node.querySelectorAll('*'));
});
});
console.log('reseted');
});
let resize_timer,
resize_listener = () => {
console.log('resizing');
if (resize_timer) clearTimeout(resize_timer);
resize_timer = setTimeout(() => {
switch (document.body.getAttribute('dark_mode_state')) {
case '0':
window.removeEventListener('resize', resize_listener);
break;
case '1':
set_color_all();
break;
}
}, 500);
};
window.addEventListener('resize', resize_listener);
let dark_style = document.createElement('style');
dark_style.textContent = `a:visited {
color: #b553ff!important
}
*{
text-shadow:none!important;
}
::before,::after{
background-color:transparent!important;
}
::-webkit-scrollbar {
overflow: hidden;
width: .8rem;
background: #000;
}
::-webkit-scrollbar-thumb {
overflow: hidden;
border-radius: .4rem;
background: #ddd;
}`;
dark_style.className = 'dark_visited_style';
document.body.appendChild(dark_style);
document.body.setAttribute('dark_mode_state', '1');
set_color_all();
observer.observe(document.body, {
childList: true,
subtree: true
});
console.log('0');
break;
case '1':
document.body.setAttribute('dark_mode_state', '0');
document.querySelectorAll('*').forEach(node => {
if (!node.style) return;
const style = node.getAttribute('dark_before_style');
if (style === null) return;
node.style.cssText = style;
});
document.body.getElementsByClassName('dark_visited_style')[0].remove();
console.log('1');
break;
}
};
if (document.readyState == 'loading') {
document.addEventListener('DOMContentLoaded', main);
} else {
main()
}
})();
- Permalink
- このページへの個別リンクです。
- RAW
- 書かれたコードへの直接のリンクです。
- Packed
- 文字列が圧縮された書かれたコードへのリンクです。
- Userscript
- Greasemonkey 等で利用する場合の .user.js へのリンクです。
- Loader
- @require やソースコードが長い場合に多段ロードする Loader コミのコードへのリンクです。
- Metadata
- コード中にコメントで @xxx と書かれたメタデータの JSON です。