Company logo
  • Jobs
  • Bootcamp
  • About Us
  • For professionals
    • Home
    • Jobs
    • Courses
    • Questions
    • Teachers
    • Bootcamp
  • For business
    • Home
    • Our process
    • Plans
    • Assessments
    • Payroll
    • Blog
    • Sales
    • Calculator

0

96
Views
Javascript create custom attribute getter (like in python)

Say I have a class in JavaScript (yes bad bad class, bad class in JS, but its for web components, one has to use classes).

I would like to create a cached attribute getter for elements on a class, in python it would be this:

class Foo(object):
    _elements = {}
    def __getattr__(self, name):
        if name in ['widget1', 'widget2', 'widget3']:  # A long list of items, don't want to create getters for each one individiually
            if not _elements.get(name):
                self._elements[name] = self.getElementById(name)     
            return self._elements[name] 
       else:
            # Default behaviour
            return object.__getattr__(self, name)

This is the closest I got, but its ugly to use:

  • One must call it as this.el['widget1']
  • Instead of this.widget1
class Foo extends HTMLElement {
    el(id) {
        // Cached get element by id
        this._els = this._els || {};
        if (!this._els[id]) {
            this._els[id] = this.getElementById(id)
        }
        return this._els[id]
    }
7 months ago · Juan Pablo Isaza
2 answers
Answer question

0

getElementById is slower

But does your cached performance gain outway extra code, code complexity and time coding?

1 PICO second equals 0.00000001 Milliseconds

<div style="display:grid;grid-template-columns:1fr 1fr">
  <test-component id="CACHED"></test-component>
  <test-component id="BYID"></test-component>
</div>
<div id="DIFF"><b>100.000 calls per run:</b></div>
<script>
  customElements.define("test-component", class extends HTMLElement {
    el(id) {
      this._els = this._els || {};
      if (!this._els[id]) this._els[id] = document.getElementById(id);
      return this._els[id]
    }
    get CACHED() {
      return this.el("CACHED");
    }
    get BYID() {
      return document.getElementById("BYID");
    }
    connectedCallback() {
      let count = 100000;
      for (let run = 1; run < 9; run++) {
        let log = [];
        for (let cnt = 0; cnt < count; cnt++) {
          const t0 = performance.now();
          this == CACHED ? this.CACHED : this.BYID;// test performance
          log.push(performance.now() - t0);
        }
        this.average = (log.reduce((a, b) => a + b) / log.length)*1e9;
        let diff = (BYID.average - CACHED.average).toPrecision(2);
        if (this == BYID) DIFF.innerHTML += `<div>Run #${run} = <b>${diff/count}</b> PICO seconds slower per call<div>`;
      }
    }
  })
</script>

7 months ago · Juan Pablo Isaza Report

0

For anyone else who has the same issue, this is what I went with, quite a lot of code upfront, but at least its easy to use, e.g. this.PROGRESS

class Foo extends HTMLElement {
    el(id, docEl=false) {
        // Cached get element by name
        // Parameter: docEl
        //  Set to true to search the document instead of the shadowRoot
        this._els = this._els || {};
        if (!this._els[id]) {
            const searchNode = docEl ? document : this.shadowRoot;
            this._els[id] = searchNode.getElementById(id)
        }
        return this._els[id]
    }

    // Document Elements
    get TEMPLATE()          { return this.el('TEMPLATE_wc-uploader', true) }
    get TEMPLATE_WC_UPLOAD(){ return this.el('TEMPLATE_wc-upload', true) }
    // Shadow Root Elements
    get PROGRESS()          { return this.el('PROGRESS') }
    get PROGRESS_HEADING()  { return this.el('PROGRESS_HEADING') }
    get DRAG_OVERLAY()      { return this.el('DRAG_OVERLAY') }
    get UPLOAD_LIST()       { return this.el('UPLOAD_LIST') }
7 months ago · Juan Pablo Isaza Report
Answer question
Find remote jobs