您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
DMM・FANZAで商品(など)のリンクに(だいたいの)フルタイトルをポップアップ
当前为
/* jshint esversion: 6 */ // ==UserScript== // @name popupTitleOverAnchorsOnDMM // @namespace https://greasyfork.org/ja/users/289387-unagionunagi // @version 0.3 // @description DMM・FANZAで商品(など)のリンクに(だいたいの)フルタイトルをポップアップ // @author unagiOnUnagi // @match *://*.dmm.co.jp/* // @match *://*.dmm.com/* // @grant none // @license GPL-2.0-or-later // ==/UserScript== function getTrimText(node) { return node.textContent.trim().replace(/\s+/g, ' '); } function addTitleAttrAll(nodes) { // a タグに子要素の img の alt から取ってきたタイトルを title 属性として追加 // alt がなかったら a タグの onclick を見てみる // onclick もダメだったら a タグ内のタイトル文字列をそのまま // DMM 動画top: altなし、div/div(title, (price)?, sammary) // VR top: altなし、span(title, price) // Idol top: altなし、span(title, price) // IdolCh top: altなし、a直下 // 見放題ch top: altなし、a直下 // FANZA 動画top pickup: altなし、span(title, maker, price) // XPathResult はウ○コ let length = nodes.length || nodes.snapshotLength; let getItem = nodes.item || nodes.snapshotItem; getItem = getItem.bind(nodes); for ( let i=0 ; i < length; i++ ) { let anchor = getItem(i); let title = anchor.getElementsByTagName('img')[0].getAttribute('alt'); if (!title) { let onclick = anchor.getAttribute('onclick'); if (onclick && onclick.startsWith('_gaq.push') && !onclick.endsWith("'');")) { title = onclick.split("'")[13]; } } if (!title) { let txt = null; for (let c of anchor.childNodes) { if (c.nodeName == '#text') { txt = c.nodeValue.trim(); if (txt) title = txt; break; } } if (!title) { for (let s of anchor.getElementsByTagName('span')) { txt = getTrimText(s); if (txt) title = txt; break; } } if (!title) { txt = getTrimText(anchor); if (txt) title = txt; } } if (title) { anchor.setAttribute('title', title); // console.log(title); // console.log(anchor.href); } } } function addTitleAttr2Rankings() { // ランキング内商品 anchorに title= 追加 // DMM DVD通販 IdolTop: altがt、タイトルがa直下 (非固定spanのtail) // FANZA 動画 ビデオtop ranking[1:]: altなし、span(rank) a直下 title br latest let ranking = document.evaluate( './/div[@id="side-rank-tab"]//li[a[img[not(@title)]]]', document.body, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null); for ( let i=0 ; i < ranking.snapshotLength; i++ ) { let li = ranking.snapshotItem(i); let alt = li.getElementsByTagName('img')[0].getAttribute('alt'); if (!alt || alt == 't') { alt = getTrimText(li); } if (alt) { for (let a of li.getElementsByTagName('a')) { a.setAttribute('title', alt); } } } } const WAIT_INTERVAL = 200; const MAX_RETRY_LIMIT = 15; function waitActualLoading(target, func, arg=null) { let waitId = null; let c = 1; const waiter = function() { let result = target(); if (result.length || c > MAX_RETRY_LIMIT) { clearInterval(waitId); func(arg()); } c++ } waitId = setInterval(waiter, WAIT_INTERVAL); } function addEvtListener2Carousels(csssels=[['div.prev,div.next', 'div.clist a:not([title])'], ['div.d-prev,div.d-next', 'ul a:not([title])']]) { // <,>ボタン onclick で商品 anchor に title= を追加するイベントリスナーを追加 csssels.forEach(function(cs) { document.querySelectorAll(cs[0]).forEach(function(button) { button.addEventListener('click', function() { const target = () => button.parentElement.querySelectorAll(cs[1]); waitActualLoading(target, addTitleAttrAll, target); }, false); }); }); } function addEvtListener2UpdateRecommends() { // resize イベント後にレコメンド内を更新するイベントリスナーを追加 let timer = 0; window.addEventListener('resize', function() { if (timer) { clearTimeout(timer); } timer = setTimeout(function() { let csssel = [['div.prev,div.next', 'div.clist a:not([title])'], ['div.d-prev,div.d-next', 'ul a:not([title])']]; csssel.forEach(function(cs) { document.querySelectorAll(cs[0]).forEach(function(button) { const target = () => button.parentElement.querySelectorAll(cs[1]); waitActualLoading(target, addTitleAttrAll, target); }); }); }, 200); }, false); } function addTitleAttr2Anchors() { // とりあえず画像つきリンクに一律処理 addTitleAttrAll( document.evaluate('.//a[.//img]', document.body, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null)); // カテゴリトップページランキング addTitleAttr2Rankings(); // ランキングの日間・週間・月間を onclick で商品 anchor に title= を追加する // イベントリスナーを追加 // DMM側で伝播を抑止しているので同じ a タグに追加 // こっちでも抑止するとページによっては副作用が起こる // DMM側で伝播を抑止しているのでこちらのイベントもしない模様? document.querySelectorAll('.s-tb-capt a:not([title])').forEach(function(tab) { tab.addEventListener('click', function() { setTimeout(addTitleAttr2Rankings, 300) }, false); }); // <,>ボタン onclick で商品 anchor に title= を追加するイベントリスナーを追加 addEvtListener2Carousels(); // 最近チェックした商品を表示するリンクあたりに onclick で商品 anchor に // title= を追加するイベントリスナーを追加 let recent = document.getElementById('recent_check_list'); if (recent) { recent.addEventListener('click', function(e) { e.stopPropagation(); if (recent.getElementsByClassName('d-modtogglelink-open').length) { waitActualLoading( () => recent.getElementsByClassName('d-modtogglelink-close'), addTitleAttrAll, () => document.querySelectorAll('div#list-recommend01 a:not([title])')); } }, false); } // この作品に出演している〜リンクあたりに onclick で let others = document.getElementById('actorother_main'); if (others) { others.addEventListener('click', function(e) { e.stopPropagation(); const targets = () => document.querySelectorAll('#list-recommend07>.clist a:not([title])'); waitActualLoading( targets, t => { addTitleAttrAll(t); addEvtListener2Carousels([['#list-recommend07>.prev,#list-recommend07>.next', 'div.clist a:not([title])']]); }, targets); }, false); } addEvtListener2UpdateRecommends(); } (function() { const hasElementsAnyId = ids => ids.some(i => document.getElementById(i)); let waitAppear = []; let waitDisappear = []; if (hasElementsAnyId(['browserecommend', 'browserecommend1'])) { waitAppear = ['#list-history-min a img[alt]',]; } else if (hasElementsAnyId(['recommend',])) { waitAppear = ['#recommend_main li a img[alt]', '#list-recommend07>div.clist li a img[alt]', '.d-modtogglelink-close,.d-modtogglelink-open']; } if (document.getElementsByClassName('loading').length) waitDisappear = ['li.loading',]; if (waitAppear.length || waitDisappear) { const isExists = targets => document.querySelectorAll(targets).length; let waitId = null; let c = 1; const waiter = function() { let isAppeared = waitAppear ? waitAppear.every(t => isExists(t)) : true; let isDisappeared = waitDisappear ? waitDisappear.every(t => !isExists(t)) : true; if ((isAppeared && isDisappeared) || c > MAX_RETRY_LIMIT) { clearInterval(waitId); setTimeout(addTitleAttr2Anchors, 300); } c++ } waitId = setInterval(waiter, WAIT_INTERVAL); } else { addTitleAttr2Anchors(); } })();