您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
修改 bilibili 新动态顶栏, 使其更方便使用
当前为
// ==UserScript== // @name bilibili 新动态顶栏简易修改 // @namespace http://unown.moe // @version 0.4.2 // @description 修改 bilibili 新动态顶栏, 使其更方便使用 // @author Unown Hearn // @license MIT License // @match *://t.bilibili.com/* // @grant none // ==/UserScript== /* history: [2017.12.1] 0.0 简单地让 bilibili 网页端新版动态的顶栏固定, 并隐藏原本冒出来的新顶栏 [2017.12.1] 0.1 让顶栏在小窗口时也完全显示, 顶栏的左部分可以自动折叠 [2017.12.1] 0.2 顶栏的搜索栏在聚焦时会伸长到 2.5 倍 [2017.12.2] 0.3 右边栏也可以自动折叠了, 顶栏左部分的主站项不会消失 [2017.12.2] 0.3.1 完善文档 [2017.12.2] 0.3.2 偷了个懒, 让收进去的项横着显示, 规避了错位问题, 另外解决了一个初始化时变成 2 行的 bug [2017.12.2] 0.3.3 修复了一行可能会变成两行的 bug (除了尺寸非常小的情况之外) [2017.12.2] 0.4 暴力解决了网络不好时需要等待网页加载而无法执行脚本的问题 [2017.12.2] 0.4.1 修复 bug, 改进浏览器支持(chrome, safari) [2017.12.2] 0.4.1 正式发布于 greasyfork(https://greasyfork.org/zh-CN/scripts/35912-bilibili-%E6%96%B0%E5%8A%A8%E6%80%81%E9%A1%B6%E6%A0%8F%E7%AE%80%E6%98%93%E4%BF%AE%E6%94%B9) [2017.12.2] 0.4.2 添加遗漏掉的协议 TODO: * 点击省略号后, 使面板保持开启, 直到点击其他位置或再点一次省略号 * 要不要解决宽度过小时会发生的问题? 那并不是应该算是正常使用... */ (function() { //'use strict'; //< 不然 safari 会报错 // 此脚本只管动态首页 if (!/^(\/|\/\?.*)$/.test(window.location.pathname)) { return; } console.log('启用了 userscript \'固定 bilibili 新动态的顶栏 1.0\''); /** * 本 userscript 是否成功初始化 * * @type boolean */ var initialized = false; /** * 顶栏 * * @type {HTMLElement} */ var bili_wrapper; /** * 顶栏的左导航栏 * * @type {HTMLElement} */ var nav_con_fl; /** * 顶栏的右导航栏 (不包含右导航栏) * * @type {HTMLElement} */ var nav_con_fr; /** * 顶栏的投稿栏 * * @type {HTMLElement} */ var nav_upload; /** * 顶栏的搜索栏 (从右导航栏分离) * * @type {HTMLElement} */ var nav_search; /** * 是否在使用备选方案. 换句话说, 是否顶栏右部分被挤压了 * * @type boolean */ var in_plan_b = false; /** * 获取窗口宽度 * * @returns {number} 窗口宽度 */ function getWindowWidth() { return $(window).width(); } /** * 获取元素的真实大小 * * @param {HTMLElement} elem */ function getRealWidth(elem) { var width; if (elem.style.display === 'none') { var old_position = elem.style.position; var old_visibility = elem.style.visibility; elem.style.position = 'absolute'; elem.style.visibility = 'hidden'; elem.style.display = 'inline'; width = elem.offsetWidth; elem.style.position = old_position; elem.style.display = 'none'; elem.style.visibility = old_visibility; return width; } return elem.offsetWidth; } /** * 调整顶栏的某一部分导航栏 * * @param {HTMLElement} nav 导航栏 * @param {number} max_width 外部认为容许的最大宽度 * @param {HashMap} [options] 更多的选项, 包括: * @param {HTMLElement} [options.plan_b] 备选可调整的导航栏 * @param {number} [options.nav_search_width_for_plan_b] 搜索框的最终长度, 在计算备选方案的长度时要用到 * @param {boolean} [options.is_left] 如果为真, 则代表正在调整的是左部分 * * @returns {number} 隐藏项的数量 */ function __adjustNav( nav, max_width, options /*plan_b, nav_search_width_for_plan_b*/) { //< plan_b 为 undefined 代表只要管好自己就行了 // console.log(["max width:", max_width]); options = options || {}; // console.log(nav, nav.querySelector(".ul")); // querySelectorAll 蠢爆了, 不能限定到仅自己的子元素... // 包含外部项的 ul var nav_ul = nav.querySelector('ul'); // "更多"的面板 var more_panel = nav_ul.querySelector('.more-panel'); // 外部项 var outside_items = Array.prototype.slice.call(nav_ul.children); // 包含"更多"按钮的项, 位于外部项的最末尾. 可能会不 display, 但位置依旧不变 var more_button_item = outside_items[outside_items.length - 1]; var min_width = getRealWidth(more_button_item); if (options.is_left) { min_width += outside_items[0].offsetWidth; // 外部项的数组要忽略掉"第一项"和"更多"按钮 outside_items = outside_items.slice(1, outside_items.length - 1); } else { // 外部项的数组要忽略掉"更多"按钮 outside_items = outside_items.slice(0, outside_items.length - 1); } if (options.plan_b) { // 有备用计划 var max_width_for_plan_b = getWindowWidth() - min_width - nav_upload.offsetWidth - options.nav_search_width_for_plan_b; if (in_plan_b) { //< 代表目前要先调整右部分 if (__adjustNav(options.plan_b, max_width_for_plan_b) === 0) { //< 右边没有隐藏项了, 代表可以试着调整左边了 in_plan_b = false; var max_width = getWindowWidth() - nav_con_fr.offsetWidth - nav_upload.offsetWidth - options.nav_search_width_for_plan_b; __adjustNav(nav, max_width, {is_left: true}); return; } else { // 右边还没调整完, 不能调整左边, 所以直接返回 return; } } else if (max_width < min_width) { //< 左边不能再压榨空间了 if (nav.offsetWidth > min_width) { __adjustNav(nav, min_width, {is_left: true}); //< 先把左边调整好 // console.log([min_width, nav.offsetWidth]); } in_plan_b = true; console.log([nav_con_fr.offsetWidth, max_width_for_plan_b]); __adjustNav(options.plan_b, max_width_for_plan_b); return; } } nav.style.maxWidth = (max_width).toString() + 'px'; // 包含隐藏项的 ul var more_panel_ul = more_panel.querySelector('ul'); // 隐藏项 var hidden_items = Array.prototype.slice.call((more_panel_ul.children)); // 外部项的总长度 var sum_width = 0; // 第一个隐藏的项(或 undefined), 用于插入新的隐藏项 let first_hidden_item = hidden_items[0] || undefined; // 记录隐藏项的数量, 会在判定是否计算"更多"按钮时使用 var hidden_count = hidden_items.length; // 如果为真, 则代表外边已经容不下更多项了 var overflowed = false; // 将会溢出的项放入"更多"面板中 for (var i = 0; i < outside_items.length; i++) { if (!overflowed) { sum_width += outside_items[i].offsetWidth; var actual_width = ((i === outside_items.length - 1) && hidden_count === 0) ? sum_width + min_width - getRealWidth(more_button_item) : sum_width + min_width; // console.log([sum_width, actual_width, getRealWidth(more_button_item), // more_button_item]); if (actual_width > max_width) { overflowed = true; } } if (overflowed) { // console.log(["hide:", i, outside_items[i]]); hidden_count++; if (first_hidden_item) { // console.log(["insert before", outside_items[i], first_hidden_item]); more_panel_ul.insertBefore(outside_items[i], first_hidden_item); } else { // console.log(["append child", outside_items[i]]); more_panel_ul.appendChild(outside_items[i]); } } } // 如果没有溢出, 试着将"更多"面板中的项取回顶栏的此半部分, 直到会溢出 if (!overflowed) { for (var i = 0; i < hidden_items.length; i++) { sum_width += hidden_items[i].offsetWidth; var actual_width = (i === hidden_items.length - 1) ? sum_width + min_width - getRealWidth(more_button_item) : sum_width + min_width; if (actual_width > max_width) { overflowed = true; break; } hidden_count--; nav_ul.insertBefore(hidden_items[i], more_button_item); } } if (overflowed) { more_button_item.style.display = 'list-item'; } else { more_button_item.style.display = 'none'; } return hidden_count; } /** * 是否正在调整顶栏的导航栏 * * @type boolean */ var in_adjusting = false; /** * 调整顶栏 * * @param {number} window_width 窗口的大小 * @param {number} [nav_search_width] 搜索栏的最终大小 * * @returns {void} */ function adjustNav(window_width, nav_search_width) { if (in_adjusting) { return; } in_adjusting = true; if (!nav_search_width) { nav_search_width = nav_search.offsetWidth; } var max_width = window_width - nav_con_fr.offsetWidth - nav_upload.offsetWidth - nav_search_width; __adjustNav(nav_con_fl, max_width, { plan_b: nav_con_fr, nav_search_width_for_plan_b: nav_search_width, is_left: true }); in_adjusting = false; } /** * * @param {*} selector 选择器 * @param {*} inv 尝试间隔 * @param {*} callback 回调函数, 以毫秒为单位 * * @returns {void} */ function waitUntilLoaded(selectors, inv, callback) { for (var i = 0; i < selectors.length; i++) { if (!document.querySelector(selectors[i])) { setTimeout(function() { waitUntilLoaded(selectors, inv, callback); }, inv); return; } } callback(); } // 在页面加载后初始化本 userscript function initialize() { // 让 home-container 能在正确的位置显示 { var home_container = document.querySelector('.home-container'); if (home_container) { home_container.style = 'position: absolute; top:42px;'; } else { console.log('home-container 不存在!'); } } // 使顶栏固定 { var header = document.querySelector('.bili-header-m'); if (header) { header.style = 'position: fixed; top:0; width: 100%;'; } else { console.log('bili-header-m 不存在!'); return; } } // ~~让顶栏的内容能正常显示~~ { bili_wrapper = document.querySelector('.bili-wrapper'); nav_con_fl = bili_wrapper.querySelector('.nav-con.fl'); nav_con_fr = bili_wrapper.querySelector('.nav-con.fr'); nav_upload = bili_wrapper.querySelector('.up-load'); if (!bili_wrapper || !nav_con_fl || !nav_con_fr || !nav_upload) { console.log( '.bili-wrapper 或 .nav-con.fl 或 .nav-con.fr 或 .up-load.fr 不存在!'); console.log([bili_wrapper, nav_con_fl, nav_con_fr, nav_upload]); return; } else { // nav_con_fl.style = "font-size: 0.8em; text-indent: -5px;"; // nav_con_fr.style = "float: left;"; // nav_upload.style = "float: left;"; // bili_wrapper.appendChild(nav_upload); // nav_con_fl.style = "overflow: hidden;"; // nav_con_fl_ul = nav_con_fl.querySelector("ul"); } } // 把搜索栏挪出来 { nav_search = bili_wrapper.querySelector('.nav-search'); if (nav_search) { bili_wrapper.appendChild(nav_search); } else { console.log('.nav-search 不存在'); } nav_search.style.marginRight = '0'; } // 添加"更多"的按钮 function addButtonMore(nav, is_right) { var ul = nav.querySelector('ul'); var item_more = document.createElement('li'); item_more.classList.add('nav-item'); item_more.classList.add('more'); item_more.style.paddingLeft = '11px'; item_more.style.paddingRight = '11px'; if (is_right) { item_more.style.display = 'none'; } var button_area = document.createElement('div'); button_area.classList.add('button-area'); button_area.classList.add('c-pointer'); // 照猫画虎 var more_button = document.createElement('div'); more_button.classList.add('icon-font'); more_button.classList.add('icon-more-1'); var more_panel = document.createElement('div'); more_panel.classList.add('more-panel'); // more_panel.style = "position: absolute; display: none; left:"; more_panel.style = 'position: absolute;'; var more_panel_ul = document.createElement('ul'); button_area.appendChild(more_button); item_more.appendChild(button_area); ul.appendChild(item_more); more_panel.appendChild(more_panel_ul); button_area.appendChild(more_panel); } addButtonMore(nav_con_fl); addButtonMore(nav_con_fr, true); // 让搜索框可以伸缩 { var search_input = document.querySelector('.nav-search input'); search_input.addEventListener('focusin', function() { // console.log(["focusin"]); adjustNav(window_width, 142); // workaround search_input.style.width = '100px'; }); search_input.addEventListener('focusout', function() { search_input.style.width = '40px'; setTimeout(function() { adjustNav(window_width); }, 250); // workaround }); } // css 相关 { var sheet = document.createElement('style'); sheet.innerHTML = // 抄自网站自己的 css, 用来显示面板 '.nav-con .more-panel {' + 'position: absolute;' + //'width: 94px;' + 'text-align: center;' + 'top: 45px;' + 'left: -4px;' + //"right: 5px;"+ 'background: #fff;' + 'border: 1px solid #e5e9ef;' + '-webkit-box-shadow: 0 11px 12px 0 rgba(106,115,133,0.12);' + 'box-shadow: 0 11px 12px 0 rgba(106,115,133,0.12);' + 'border-radius: 8px;' + 'color: #222;' + 'z-index: 10;' + // 适应大小 'width: -moz-max-content;' + // firefox 'width: max-content;' + // chrome 'width: -webkit-max-content;' + // safari '}\n' + // 右边 '.nav-con.fr .more-panel {' + 'right: 4px;' + 'left: auto;' + '}\n' + // 抄自网站自己的 css, 用来显示面板的角 '.nav-con .more-panel:after {' + 'content: "";' + 'display: block;' + 'border-top: 1px solid #e5e9ef;' + 'border-left: 1px solid #e5e9ef;' + '-webkit-transform: rotate(45deg);' + 'transform: rotate(45deg);' + 'width: 8px;' + 'height: 8px;' + 'position: absolute;' + 'top: -5px;' + 'left: 12px;' + 'background: #fff;' + '}\n' + // 右边 '.nav-con.fr .more-panel:after {' + 'right: 12px;' + 'left: auto;' + '}\n' + // ~~让更多面板上的项居中~~ 与面板中的项有关的样式 '.more-panel .nav-item {' + //'float: none!important;' + 'transition: none!important;' + //"clear: both;" + '}\n' + // 自适应宽度 '@media screen and (max-width: 980px) {' + '.bili-header-m .bili-wrapper {' + 'width: 100%;' + '}' + '}\n' + // 悬浮显示面板 '.nav-item.more .more-panel {' + 'visibility: hidden;' + 'transition-duration: 0.1s;' + 'transition-delay: 0.1s;' + '}\n' + '.nav-item.more:hover .more-panel {' + 'visibility: visible;' + 'transition-duration: 0.1s;' + 'transition-delay: 0s;' + '}\n' + // 让 home 的位置好看些 '.nav-item.home { ' + 'margin-left: 0!important;' + 'transition-duration: 0s;' + 'transition-delay: 0s;' + '}\n'; document.head.appendChild(sheet); } // setTimeout(function() { adjustNav(getWindowWidth()); initialized = true; //}, 1000); // 隐藏会冒出来的新顶栏 { var sticky_bar = document.querySelector('.sticky-bar'); if (sticky_bar) { sticky_bar.style = 'visibility: hidden;'; } else { console.log('sticky-bar 不存在!'); } } } waitUntilLoaded(['.nav-con.fl', '.nav-con.fr', '.up-load.fr'], 50, initialize); /** * 用于检测是否是窗口的宽度发生了变化 * * @type {number} */ var window_width = getWindowWidth(); // 监视窗口调整大小 window.addEventListener('resize', function() { if (!initialized) { return; } var new_width = getWindowWidth(); if (window_width === new_width) { return; } window_width = new_width; // nav_con_fl, nav_con_fr, nav_upload; adjustNav(window_width); // console.log(nav_con_fl.style.maxWidth); }); })();