野声

Hey, 野声!

谁有天大力气可以拎着自己飞呀
twitter
github

Using UserScript in Angular web pages

As a front-end developer, I occasionally write user scripts1 to enhance my browsing experience.

For traditional server-rendered pages, you can directly execute scripts on the page to achieve your goals. However, for modern data-driven front-end development frameworks, such as Angular, the old approach no longer works. You need to find a way to participate in the rendering of the page.

So, how can we modify an Angular page?

First of all, I want to clarify that I mainly work with React, so please correct me if I make any mistakes.

Accessing the Angular Instance#

After doing some Google searches, I found this gist that allows you to directly access the Angular instance.

Let me explain the code:
Since Angular injects itself into window.angular, we can use the angular.element method to generate an Angular object.

Follow the instructions in the gist to execute the code snippet.

Once we have access to the Angular instance, it becomes easier to debug and analyze the data flow of the web page. We can watch on the rootScope and obtain the corresponding scope for modification.

Analyzing the Data Flow of the Web Page#

btn-list.png

Normally, we cannot click on this user action button because the disabled attribute is set to true. To inspect the DOM of this button, enter $scope in the console (you need to execute the code snippet in the web page as mentioned in the gist).

btn-scope-info.png

Try modifying $scope.disabled = false and then click on the button. It should be clickable now.

btn-can-click.png

However, if we switch to another page and then switch back, we won't be able to click on the button again. The root cause is that we haven't affected the innermost data flow.

Listening to Page Changes and Continuously Modifying Data#

The following script was originally based on an Angular watch-related question on StackOverflow. Unfortunately, I couldn't find the link to the original question, so I apologize for not providing proper attribution.

So, how can we access the Angular object in our user script?

var retryCount = -1,
  maxRetry = 5,
  timeout = 1000,
  timer;
(function initScript() {
  window.clearTimeout(timer);
  if (!window.angular) {
    retryCount++;
    if (retryCount < maxRetry) {
      timer = setTimeout(initScript, timeout);
    }
    return;
  }
  setTimeout(injectScript, 1000);
})();

We can block and wait for Angular to load successfully. Then, execute injectScript to run our own script.

function injectScript() {
  var ngAppElem = angular.element(
    document.querySelector('[ng-app]') || document
  );
  $rootScope = ngAppElem.scope();
  $rootScope.$watch(function () {
    // do something.
  });
}

We execute watch on the rootScope, so it won't be lost when switching pages.

function injectScript() {
  var ngAppElem = angular.element(
    document.querySelector('[ng-app]') || document
  );
  $rootScope = ngAppElem.scope();
  $rootScope.$watch(function () {
    var elem = angular.element(
      document.querySelector(
        '#dashboardmainpart > div > div.EventBottomChartsContainer > div.EventDetailContainer > div > ul > li:nth-child(3)'
      )
    );
    var s1 = elem.isolateScope() || elem.scope();
    if (s1) {
      s1.disabled = false;
      return s1.disabled;
    }
    return s1;
  });
}

Then, we retrieve the scope of the desired location and modify it directly. The watch function will be executed every time the page is modified.

You can refer to the complete demo here:

Footnotes#

  1. https://en.wikipedia.org/wiki/Userscript

Loading...
Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.