@sv443-network/userutils 4.0.0 → 4.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # @sv443-network/userutils
2
2
 
3
+ ## 4.1.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 885323d: Added function `observeElementProp` to allow observing element property changes
8
+
3
9
  ## 4.0.0
4
10
 
5
11
  ### Major Changes
package/README.md CHANGED
@@ -33,6 +33,7 @@ View the documentation of previous major releases: [3.0.0](https://github.com/Sv
33
33
  - [interceptEvent()](#interceptevent) - conditionally intercepts events registered by `addEventListener()` on any given EventTarget object
34
34
  - [interceptWindowEvent()](#interceptwindowevent) - conditionally intercepts events registered by `addEventListener()` on the window object
35
35
  - [isScrollable()](#isscrollable) - check if an element has a horizontal or vertical scroll bar
36
+ - [observeElementProp()](#observeelementprop) - observe changes to an element's property that can't be observed with MutationObserver
36
37
  - [**Math:**](#math)
37
38
  - [clamp()](#clamp) - constrain a number between a min and max value
38
39
  - [mapRange()](#maprange) - map a number from one range to the same spot in another range
@@ -675,6 +676,59 @@ console.log("Element has a vertical scroll bar:", vertical);
675
676
 
676
677
  </details>
677
678
 
679
+ <br>
680
+
681
+ ### observeElementProp()
682
+ Usage:
683
+ ```ts
684
+ observeElementProp(
685
+ element: Element,
686
+ property: string,
687
+ callback: (oldValue: any, newValue: any) => void
688
+ ): void
689
+ ```
690
+
691
+ This function observes changes to the given property of a given element.
692
+ While regular HTML attributes can be observed using a MutationObserver, this is not always possible for properties that are assigned on the JS object.
693
+ This function shims the setter of the provided property and calls the callback function whenever it is changed through any means.
694
+
695
+ When using TypeScript, the types for `element`, `property` and the arguments for `callback` will be automatically inferred.
696
+
697
+ <details><summary><b>Example - click to view</b></summary>
698
+
699
+ ```ts
700
+ import { observeElementProp } from "@sv443-network/userutils";
701
+
702
+ const myInput = document.querySelector("input#my-input");
703
+
704
+ let value = 0;
705
+
706
+ setInterval(() => {
707
+ value += 1;
708
+ myInput.value = String(value);
709
+ }, 1000);
710
+
711
+
712
+ const observer = new MutationObserver((mutations) => {
713
+ // will never be called:
714
+ console.log("MutationObserver mutation:", mutations);
715
+ });
716
+
717
+ // one would think this should work, but "value" is a JS object *property*, not a DOM *attribute*
718
+ observer.observe(myInput, {
719
+ attributes: true,
720
+ attributeFilter: ["value"],
721
+ });
722
+
723
+
724
+ observeElementProp(myInput, "value", (oldValue, newValue) => {
725
+ // will be called every time the value changes:
726
+ console.log("Value changed from", oldValue, "to", newValue);
727
+ });
728
+ ```
729
+
730
+ </details>
731
+
678
732
  <br><br>
679
733
 
680
734
  <!-- #SECTION Math -->
@@ -9,7 +9,7 @@
9
9
  // ==UserLibrary==
10
10
  // @name UserUtils
11
11
  // @description Library with various utilities for userscripts - register listeners for when CSS selectors exist, intercept events, manage persistent user configurations, modify the DOM more easily and more
12
- // @version 4.0.0
12
+ // @version 4.1.0
13
13
  // @license MIT
14
14
  // @copyright Sv443 (https://github.com/Sv443)
15
15
 
@@ -333,6 +333,28 @@ var UserUtils = (function (exports) {
333
333
  horizontal: (overflowX === "scroll" || overflowX === "auto") && element.scrollWidth > element.clientWidth
334
334
  };
335
335
  }
336
+ function observeElementProp(element, property, callback) {
337
+ const elementPrototype = Object.getPrototypeOf(element);
338
+ if (elementPrototype.hasOwnProperty(property)) {
339
+ const descriptor = Object.getOwnPropertyDescriptor(elementPrototype, property);
340
+ Object.defineProperty(element, property, {
341
+ get: function() {
342
+ var _a;
343
+ return (_a = descriptor == null ? void 0 : descriptor.get) == null ? void 0 : _a.apply(this, arguments);
344
+ },
345
+ set: function() {
346
+ var _a;
347
+ const oldValue = this[property];
348
+ (_a = descriptor == null ? void 0 : descriptor.set) == null ? void 0 : _a.apply(this, arguments);
349
+ const newValue = this[property];
350
+ if (typeof callback === "function") {
351
+ callback.bind(this, oldValue, newValue);
352
+ }
353
+ return newValue;
354
+ }
355
+ });
356
+ }
357
+ }
336
358
 
337
359
  // lib/misc.ts
338
360
  function autoPlural(word, num) {
@@ -585,6 +607,7 @@ var UserUtils = (function (exports) {
585
607
  exports.interceptWindowEvent = interceptWindowEvent;
586
608
  exports.isScrollable = isScrollable;
587
609
  exports.mapRange = mapRange;
610
+ exports.observeElementProp = observeElementProp;
588
611
  exports.openInNewTab = openInNewTab;
589
612
  exports.pauseFor = pauseFor;
590
613
  exports.preloadImages = preloadImages;
package/dist/index.js CHANGED
@@ -312,6 +312,28 @@ function isScrollable(element) {
312
312
  horizontal: (overflowX === "scroll" || overflowX === "auto") && element.scrollWidth > element.clientWidth
313
313
  };
314
314
  }
315
+ function observeElementProp(element, property, callback) {
316
+ const elementPrototype = Object.getPrototypeOf(element);
317
+ if (elementPrototype.hasOwnProperty(property)) {
318
+ const descriptor = Object.getOwnPropertyDescriptor(elementPrototype, property);
319
+ Object.defineProperty(element, property, {
320
+ get: function() {
321
+ var _a;
322
+ return (_a = descriptor == null ? void 0 : descriptor.get) == null ? void 0 : _a.apply(this, arguments);
323
+ },
324
+ set: function() {
325
+ var _a;
326
+ const oldValue = this[property];
327
+ (_a = descriptor == null ? void 0 : descriptor.set) == null ? void 0 : _a.apply(this, arguments);
328
+ const newValue = this[property];
329
+ if (typeof callback === "function") {
330
+ callback.bind(this, oldValue, newValue);
331
+ }
332
+ return newValue;
333
+ }
334
+ });
335
+ }
336
+ }
315
337
 
316
338
  // lib/misc.ts
317
339
  function autoPlural(word, num) {
@@ -564,6 +586,7 @@ exports.interceptEvent = interceptEvent;
564
586
  exports.interceptWindowEvent = interceptWindowEvent;
565
587
  exports.isScrollable = isScrollable;
566
588
  exports.mapRange = mapRange;
589
+ exports.observeElementProp = observeElementProp;
567
590
  exports.openInNewTab = openInNewTab;
568
591
  exports.pauseFor = pauseFor;
569
592
  exports.preloadImages = preloadImages;
package/dist/index.mjs CHANGED
@@ -310,6 +310,28 @@ function isScrollable(element) {
310
310
  horizontal: (overflowX === "scroll" || overflowX === "auto") && element.scrollWidth > element.clientWidth
311
311
  };
312
312
  }
313
+ function observeElementProp(element, property, callback) {
314
+ const elementPrototype = Object.getPrototypeOf(element);
315
+ if (elementPrototype.hasOwnProperty(property)) {
316
+ const descriptor = Object.getOwnPropertyDescriptor(elementPrototype, property);
317
+ Object.defineProperty(element, property, {
318
+ get: function() {
319
+ var _a;
320
+ return (_a = descriptor == null ? void 0 : descriptor.get) == null ? void 0 : _a.apply(this, arguments);
321
+ },
322
+ set: function() {
323
+ var _a;
324
+ const oldValue = this[property];
325
+ (_a = descriptor == null ? void 0 : descriptor.set) == null ? void 0 : _a.apply(this, arguments);
326
+ const newValue = this[property];
327
+ if (typeof callback === "function") {
328
+ callback.bind(this, oldValue, newValue);
329
+ }
330
+ return newValue;
331
+ }
332
+ });
333
+ }
334
+ }
313
335
 
314
336
  // lib/misc.ts
315
337
  function autoPlural(word, num) {
@@ -545,4 +567,4 @@ tr.getLanguage = () => {
545
567
  return curLang;
546
568
  };
547
569
 
548
- export { ConfigManager, SelectorObserver, addGlobalStyle, addParent, autoPlural, clamp, compress, debounce, decompress, fetchAdvanced, getUnsafeWindow, insertAfter, insertValues, interceptEvent, interceptWindowEvent, isScrollable, mapRange, openInNewTab, pauseFor, preloadImages, randRange, randomId, randomItem, randomItemIndex, randomizeArray, takeRandomItem, tr };
570
+ export { ConfigManager, SelectorObserver, addGlobalStyle, addParent, autoPlural, clamp, compress, debounce, decompress, fetchAdvanced, getUnsafeWindow, insertAfter, insertValues, interceptEvent, interceptWindowEvent, isScrollable, mapRange, observeElementProp, openInNewTab, pauseFor, preloadImages, randRange, randomId, randomItem, randomItemIndex, randomizeArray, takeRandomItem, tr };
package/dist/lib/dom.d.ts CHANGED
@@ -50,3 +50,13 @@ export declare function isScrollable(element: Element): {
50
50
  vertical: boolean;
51
51
  horizontal: boolean;
52
52
  };
53
+ /**
54
+ * Executes the callback when the passed element's property changes.
55
+ * Contrary to an element's attributes, properties can usually not be observed with a MutationObserver.
56
+ * This function shims the getter and setter of the property to invoke the callback.
57
+ *
58
+ * [Source](https://stackoverflow.com/a/61975440)
59
+ * @param property The name of the property to observe
60
+ * @param callback Callback to execute when the value is changed
61
+ */
62
+ export declare function observeElementProp<TElem extends Element = HTMLElement, TPropKey extends keyof TElem = keyof TElem>(element: TElem, property: TPropKey, callback: (oldVal: TElem[TPropKey], newVal: TElem[TPropKey]) => void): void;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sv443-network/userutils",
3
- "version": "4.0.0",
3
+ "version": "4.1.0",
4
4
  "description": "Library with various utilities for userscripts - register listeners for when CSS selectors exist, intercept events, manage persistent user configurations, modify the DOM more easily and more",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",