简体   繁体   中英

Edit audio element volume in userscript

I'm writing a userscript to try to decrease an overly loud audio element. How do I get access to an audio element that has been created, but not placed in the DOM anywhere?

On the site itself, it has something akin to:

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();
    };

    //...
}();

In my userscript, I want to do something like

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();
    };

    // ...
}();

I can't seem to get access to it, though. I don't really understand where these objects live. If they're not in the DOM, how can they be played? They just seem to exist in the document somewhere since they can be played, but it is not obvious to me how to find them.

Because the callbackFn is called asynchronously, you can alter HTMLMediaElement.prototype 's setter on its volume property, so that changes to the volume on an element with the src you're interested in will result in the original setter being called with a volume 1/10th of what it would be called with by default:

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);
  }
});

Of course, note that this is only a hack for when you can't alter the existing JS of a page, such as with a userscript.

I'm looking for a way to access audio elements that exist in the webpage but have not been added to the DOM

Pretty sure the only way to do something like this would be to use a hack like overwriting document.createElement with code that runs before the page's code that uses createElement runs.

Given the code at the question and the fact that autoplay policy of modern browsers have changed, one you locate which element in the DOM begins media playback after user action you can remove and replace the event handler with a function which sets volume and calls the original handler, for example

<!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>

plnkr https://plnkr.co/edit/fNlhODuwC5Z0jXchoWBE?p=preview

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM