Javascript 在userscript中编辑音频元素音量

Javascript 在userscript中编辑音频元素音量,javascript,html5-audio,userscripts,Javascript,Html5 Audio,Userscripts,我正在编写一个用户脚本,试图减少过于响亮的音频元素。如何访问已创建但未放置在DOM anywhere中的音频元素 在网站本身,它有一些类似于: function () { var audioElement = document.createElement('audio'); audioElement.setAttribute('src', 'http://www.example.com/sound.mp3'); callbackFn = function() {

我正在编写一个用户脚本,试图减少过于响亮的音频元素。如何访问已创建但未放置在DOM anywhere中的音频元素

在网站本身,它有一些类似于:

function () {
    var audioElement = document.createElement('audio');
    audioElement.setAttribute('src', 'http://www.example.com/sound.mp3');

    callbackFn = function() {
        audioElement.volume = way_too_high;
        audioElement.load();
        audioElement.play();
    };

    //...
}();
在我的userscript中,我想做如下事情

function () {
    newCallbackFunction = function() {
        var audioElement = document.querySelector('audio'); // doesn't work, presumably because never added to DOM
        audioElement.volume = 0.1 * audioElement.volume;
        audioElement.load();
        audioElement.play();
    };

    // ...
}();

不过,我似乎无法访问它。我真的不明白这些东西住在哪里。如果它们不在DOM中,如何播放它们?它们似乎存在于文档中的某个地方,因为它们可以播放,但我不知道如何找到它们。

因为
callbackFn
是异步调用的,所以可以更改
HTMLMEDIALEMENT。prototype
的setter在其
volume
属性上,因此,对您感兴趣的带有
src
的元素上的卷所做的更改将导致调用原始setter时使用默认值的1/10卷:

const { prototype } = HTMLMediaElement;
const { set: setter, get: getter } = Object.getOwnPropertyDescriptor(prototype, 'volume');
Object.defineProperty(prototype, 'volume', {
  get() {
    return getter.call(this);
  },
  set(arg) {
    const newArg = this.src.endsWith('/sounds/warframe_alarm.mp3')
    ? arg / 10
    : arg;
    setter.call(this, newArg);
  }
});
当然,请注意,这只是当您无法更改页面的现有JS时的一种攻击,例如使用用户脚本

我正在寻找一种方法来访问网页中存在但尚未添加到DOM中的音频元素


可以肯定的是,这样做的唯一方法是使用类似于重写
文档的黑客。createElement
的代码在使用
createElement
的页面代码运行之前运行。

考虑到问题中的代码以及现代浏览器的
自动播放
策略已经改变的事实,如果您找到
DOM
中的哪个元素在用户操作后开始媒体播放,则可以删除事件处理程序,并使用设置
volume
并调用原始处理程序的函数替换事件处理程序,例如

<!DOCTYPE html>
<html>

<head>
</head>

<body>
  click
  <script>
    (function() {
      var audioElement = document.createElement('audio');
      audioElement.setAttribute('src', 'https://upload.wikimedia.org/wikipedia/commons/6/6e/Micronesia_National_Anthem.ogg');

      callbackFn = function() {
        audioElement.load();
        audioElement.play();
      };
      document.onclick = callbackFn;

      //...
      function intercept(volume, audioElement) {
        const fn = document.onclick;
        document.onclick = null;
        document.onmouseup = function() {
          audioElement.volume = volume;
          fn();
        };
      }

      intercept(.2, audioElement)
    })();
  </script>
</body>

</html>

点击
(功能(){
var audioElement=document.createElement('audio');
audioElement.setAttribute('src','https://upload.wikimedia.org/wikipedia/commons/6/6e/Micronesia_National_Anthem.ogg');
callbackFn=函数(){
audioElement.load();
audioElement.play();
};
document.onclick=callbackFn;
//...
功能截取(音量、音频元素){
const fn=document.onclick;
document.onclick=null;
document.onmouseup=函数(){
audioElement.volume=音量;
fn();
};
}
截距(.2,音频元素)
})();

plnkr

该站点的Javascript多久运行一次?在函数执行之前,你能自己运行任何东西吗?(可能会发布一个到站点的链接,这样我们就可以自己测试解决方案了?)可以创建和播放音频对象,而不必成为DOM层次结构的一部分。在这种情况下,您需要手动跟踪音频对象的集合,而不是依靠文档查询来获取音频对象。考虑到最近更改的浏览器自动播放策略,一些用户操作应该调用
callbackFn
,否则
htmlmediaoelement
不应该以任何音量输出音频。什么用户操作会触发音频播放?@CertainPerformance FWIW如果问题OP中包含了所有代码,则只需在
单击
或参考
audioElement
触发媒体播放的其他用户操作事件处移除并替换处理程序即可<代码>让a=Element附加ClickEvent;a、 removeEventListener('click',callbackFn);a、 addEventListener('click',modifiedCallbackFn/*set volume*/)@CertainPerformance是我写这篇文章的目的地,当前的用户脚本正在(虽然目前不起作用)现场演示:(虽然只需将代码粘贴到OP页面上的用户脚本中比较容易,但它确实起作用。)
.volume
可以随时设置,无论何时设置
.src
,这种方法都完全忽略
src
部分,只截获更改
.volume
的调用