Minvy
Minvy

Reputation: 19

Javascript variable as function name

const self = {
        element: document.querySelector(selector),
        html: () => self.element,
        on: (event, callback) => {
            self.element.addEventListener(event, callback);
        },
        style: {
            alignContent: (property) => {
                return (property === null) ? self.element.style.alignContent : self.element.style.alignContent = property;
            }
     }
}

I am trying to make it so I have quick access to all CSS style properties with jQuery like selectors it should work as: select('h1').style.alignContent('center'), but the problem is that I would have to make a seperate function for each style property in order for this method to work, is there a way to solve this problem without duplicating a lot of code?

//Duplication example
color: (property) => {
  return (property === null) ? self.element.style.color : self.element.style.color = property;
}

Upvotes: 0

Views: 90

Answers (3)

Gershom Maes
Gershom Maes

Reputation: 8130

One way to do this is with a Proxy (mdn):

let elemWrapper = selector => {
  let element = document.querySelector(selector);
  return {
    element,
    html: () => element,
    on: (event, callback) => {
      element.addEventListener(event, callback);
    },
    style: new Proxy({}, {
      get: (obj, prop) => {
        // The user called a function named "prop"
        // We need to return a function that sets the style property named "prop"
        return cssValue => element.style[prop] = cssValue;
      }
    })
  };
};

let bodyElem = elemWrapper('body');
bodyElem.style.backgroundColor('cyan');

Here to prove the concept I've set the body element's background colour using a dynamically named function.

The big downside to this approach is the poor performance of Proxies (an excellent read on Proxy performance is available here).

This means it may be quicker to simply compile a list of all css property names, and define a function for each (never using Proxies). The following code compiles all css property names, to serve as a starting point:

console.log(Object.keys(document.body.style));

Upvotes: 4

Unmitigated
Unmitigated

Reputation: 89149

You can use a Proxy to intercept all attempts to get a property.

let selector = '#test';
const self = {
    element: document.querySelector(selector),
    html: () => self.element,
    on: (event, callback) => {
        self.element.addEventListener(event, callback);
    },
    style: new Proxy(Object.create(null), {
        get(target, prop, receiver) {
            if (self.element.style.hasOwnProperty(prop)) {
                return val => {
                    if (val != null) {
                        self.element.style[prop] = val;
                    } else {
                        return self.element.style[prop];
                    }
                }
            }
            throw Error("No such property exists: " + prop);
        }
    })
};
self.style.color('red')
console.log("Color:", self.style.color());
<div id="test">
This is a test
</div>

You can also wrap this into a general function like so:

const getEnhancedElement = arg => {
  const element = /Element/.test(Object.prototype.toString.call(arg)) ? arg
     : document.querySelector(arg);//accept a HTMLElement or a selector
  return {
    element,
    html: () => element,
    on: (event, callback) => {
      element.addEventListener(event, callback);
    },
    style: new Proxy(Object.create(null), {
      get(target, prop) {
        if (element.style.hasOwnProperty(prop)) {
          return val => {
            if (val != null) {//set value
              element.style[prop] = val;
            } else {//get value
              return element.style[prop];
            }
          }
        }
        throw Error("No such property exists: " + prop);
      }
    })
  };
};
let test = getEnhancedElement("#test");
test.style.color('red')
console.log("Color:", test.style.color());
test.style.textAlign('center');
<div id="test">
  This is a test
</div>

Upvotes: 1

Asutosh
Asutosh

Reputation: 1818

I would have something like this:

style: {
    chnageStyle: (propertyName, propertyVal) => {
        return (propertyName === null) ? self.element.style[propertyName] : self.element.style[propertyName] = propertyVal;
    }
}

Then you can call this:

style.changeStyle('alignContent','center');
style.changeStyle('color','orange');

Upvotes: 0

Related Questions