您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Makes SHOW LESS button to be "sticky" to the video description section, so you can easily fold a long description without scrolling it all the way to its bottom.
- /*
- Youtube sticky Show Less button: Makes SHOW LESS button to be "sticky"
- to the video description section, so you can easily fold a long description
- without scrolling it all the way to its bottom.
- Copyright (C) 2023 T1mL3arn
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
- */
- // ==UserScript==
- // @name Youtube sticky Show Less button
- // @description Makes SHOW LESS button to be "sticky" to the video description section, so you can easily fold a long description without scrolling it all the way to its bottom.
- // @description:RU Делает кнопку СВЕРНУТЬ в описании видео "липкой". Чтобы свернуть длинное описание теперь не нужно прокручивать это описание в самый низ.
- // @namespace https://github.com/t1ml3arn-userscript-js
- // @version 1.3.1
- // @match https://www.youtube.com/*
- // @match https://youtube.com/*
- // @noframes
- // @grant none
- // @author T1mL3arn
- // @homepageURL https://github.com/t1ml3arn-userscript-js/Youtube-sticky-SHOW-LESS-button
- // @supportURL https://github.com/t1ml3arn-userscript-js/Youtube-sticky-SHOW-LESS-button/issues
- // @license GPL-3.0-only
- // ==/UserScript==
- const SHOWLESS_BTN_WRAP_CLS = 'sticky-show-less-btn-wrap';
- const STICKY_STYLE_ELT_ID = 'sticky-states-css'
- const STICKY_STYLESHEET_CONTENT = `
- #description-inline-expander {
- /*
- To make stickiness work I have to set "overflow: visible" on this element.
- Without this SHOW LESS button sticks with wrong way and does not work as intended.
- Sticky elt sticks to its nearest ancestor that has a "scrolling mechanism"
- (created when overflow is hidden, scroll, auto, or overlay)
- So, disabling "show less" button parent's "scrolling mechanism"
- it is possible to make it work as expected.
- Still, I neither know nor understand how this shit works.
- See more:
- - https://developer.mozilla.org/en-US/docs/Web/CSS/position#sticky
- - https://uxdesign.cc/position-stuck-96c9f55d9526#b2ca
- */
- overflow: visible !important;
- }
- .${SHOWLESS_BTN_WRAP_CLS} {
- position: sticky;
- bottom: 50px;
- text-align: right;
- bottom: 50%;
- pointer-events: none;
- z-index: 999;
- }
- tp-yt-paper-button#collapse {
- pointer-events: initial;
- padding: 6px 16px;
- background: darkseagreen;
- color: white;
- margin-top: 0;
- }
- `;
- let SETTINGS = {
- videoDescriptionSelector: 'ytd-video-secondary-info-renderer',
- videoTitleSelector: 'div#info.ytd-watch-flexy',
- showLessBtnSelector: 'ytd-expander.ytd-video-secondary-info-renderer tp-yt-paper-button#less.ytd-expander',
- }
- function initOldDesignSettings() {
- const css = `
- ytd-page-manager {
- /*
- To make stickiness work I have to set "overflow: visible" on this element.
- Without this SHOW LESS button sticks with wrong way and does not work as intended.
- See more:
- - https://developer.mozilla.org/en-US/docs/Web/CSS/position#sticky
- - https://uxdesign.cc/position-stuck-96c9f55d9526#b2ca
- */
- overflow: visible !important;
- }
- .${SHOWLESS_BTN_WRAP_CLS} {
- position: sticky;
- bottom: 50px;
- text-align: right;
- bottom: 50%;
- pointer-events: none;
- }
- ytd-video-secondary-info-renderer tp-yt-paper-button#less.ytd-expander {
- pointer-events: initial;
- padding: 6px 16px;
- background: darkseagreen;
- }
- ytd-video-secondary-info-renderer tp-yt-paper-button#less.ytd-expander > .less-button {
- color: white;
- margin-top: 0;
- }
- `;
- SETTINGS = {
- ...SETTINGS,
- videoDescriptionSelector: 'ytd-video-secondary-info-renderer',
- videoTitleSelector: 'div#info.ytd-watch-flexy',
- showLessBtnSelector: 'ytd-expander.ytd-video-secondary-info-renderer tp-yt-paper-button#less.ytd-expander',
- css,
- }
- }
- function addCss(css, id) {
- const style = document.head.appendChild(document.createElement('style'))
- style.textContent = css;
- style.id = id;
- }
- function getVisibleElt(selector) {
- return Array.from(document.querySelectorAll(selector)).find(e => e.offsetParent != null)
- }
- function fixScroll() {
- if (areCommentsVisible())
- preserveCommentsOnScreen()
- else if (isDescriptionTopEdgeOutView())
- scrollDescriptionIntoView();
- else {
- console.debug('do nothing with scroll')
- }
- }
- function areCommentsVisible() {
- const vpHeight = window.visualViewport.height
- const commentsTop = getVisibleElt('ytd-comments').getBoundingClientRect().top
- return commentsTop < vpHeight;
- }
- function preserveCommentsOnScreen() {
- const descriptionElt = document.querySelector(SETTINGS.videoDescriptionSelector)
- // scrollOffset must not be negative!
- const scrollOffset = Math.abs(descriptionElt.getBoundingClientRect().height - descriptionHeight)
- let { scrollX, scrollY } = window;
- console.debug('preserve comments:', scrollY, scrollOffset, scrollY - scrollOffset)
- scrollY = scrollY - scrollOffset;
- window.scrollTo(scrollX, scrollY)
- }
- function isDescriptionTopEdgeOutView() {
- const descriptionElt = document.querySelector(SETTINGS.videoTitleSelector);
- return descriptionElt.getBoundingClientRect().top < 0
- }
- function scrollDescriptionIntoView() {
- console.debug('scroll description into view')
- document.querySelector(SETTINGS.videoTitleSelector).scrollIntoView({ behavior: 'smooth' })
- }
- let descriptionHeight;
- function saveDescriptionHeight() {
- // saving initial description elt height (it is needed to fix scroll position)
- const descriptionElt = document.querySelector(SETTINGS.videoDescriptionSelector)
- descriptionHeight = descriptionElt.getBoundingClientRect().height;
- // at saveDescriptionHeight() call height might be not actual,
- // and delaying the reading helps
- setTimeout(() => {
- descriptionHeight = document.querySelector(SETTINGS.videoDescriptionSelector).getBoundingClientRect().height
- }, 0);
- }
- function enchanceShowLessButton() {
- for (const showLessBtn of document.querySelectorAll(SETTINGS.showLessBtnSelector)) {
- const showLessParent = showLessBtn.parentElement
- const btnWrap = document.createElement('div')
- btnWrap.appendChild(showLessBtn)
- // I use wrap to intercept clicks in CAPTURE phase
- // to calcalute scroll offset BEFORE youtube hides the description
- btnWrap.addEventListener('click', fixScroll, true)
- const stickyWrap = document.createElement('div');
- stickyWrap.classList.add(SHOWLESS_BTN_WRAP_CLS)
- stickyWrap.appendChild(btnWrap);
- // add sticky wrapper (with showless button) to video description element
- showLessParent.appendChild(stickyWrap)
- }
- }
- function init() {
- // Looks like 'yt-page-data-updated' is the event I need to listen
- // to know exactly when youtube markup is ready to be queried.
- document.addEventListener('yt-page-data-updated', _ => {
- // Script should work only for pages with a video,
- // such pages have url like https://www.youtube.com/watch?v=25YbRHAc_h4
- if (window.location.search.includes('v=')) {
- // settings for the actual design
- SETTINGS = {
- videoDescriptionSelector: '#above-the-fold.ytd-watch-metadata',
- videoTitleSelector: '#above-the-fold.ytd-watch-metadata',
- showLessBtnSelector: 'tp-yt-paper-button#collapse',
- css: STICKY_STYLESHEET_CONTENT,
- }
- addCss(SETTINGS.css, STICKY_STYLE_ELT_ID)
- try {
- saveDescriptionHeight()
- enchanceShowLessButton()
- } catch (e) {}
- setTimeout(() => {
- // if for some reason there is old design in use
- const elt = document.querySelector('ytd-video-secondary-info-renderer.ytd-watch-flexy')
- if (elt?.offsetParent != null) {
- console.log('OLD design detected');
- initOldDesignSettings()
- document.getElementById(STICKY_STYLE_ELT_ID)?.remove();
- addCss(SETTINGS.css, STICKY_STYLE_ELT_ID)
- saveDescriptionHeight()
- enchanceShowLessButton()
- }
- }, 125);
- }
- })
- }
- init()
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址