6

I'm starting to develop a small JavaScript library and I want to make styling of HTML elements possible only through my API (because for some reason I need to have full control over styling).

So I want to make style property inaccessible (my API will access it through my style alias - not an ideal solution, but for other libraries like jQuery it will do the trick).

If I write this (inspired by this topic):

var box = document.getElementById('someElementId');
Object.defineProperty(box, 'style', {
    get: function() {
        throw 'you cant access style property';
    }
});
box.style.color = 'red';

it works for box element only.

Is it possible to do this for all (existing and future) elements in Webkit, Firefox, and IE9+?

I've also tried this:

Object.defineProperty(HTMLElement, 'style', {...

but no luck.

Any help would be greatly appreciated!

Edit

As @Teemu suggested I can write HTMLElement.prototype instead of HTMLElement, and it works fine in FF and IE, but not in Chrome. And it looks like a Chrome bug. Sadly...

Edit2 - why do I need it

The main goal of a library I want to develop is to allow writing styles like:

element.setWidth('parent.width / 2 - 10');

In this case element's width should react on each changing of the parent's width.
But since onresize event is available only for window object (this article seems to be obsolete), the only way I can "listen" modifying .style.width property is to perform my own API for styling.
And I want to restrict (or at least show warning) direct style modifying because it will break the elements' behavior.

Community
  • 1
  • 1
Oleg
  • 22,300
  • 9
  • 68
  • 84
  • 4
    Just curious, if style is inaccessible, how will your API set it? – Gary Mar 15 '14 at 13:18
  • 2
    `Object.defineProperty(HTMLElement.prototype, 'style', {...})` would do the trick. How you'll set the `style` in your API then, I don't know... – Teemu Mar 15 '14 at 13:19
  • @Teemu, @Gary I think of something like this: `box._css = box.style;` (before restricting), and then access to `._css` property. Of cource this is not a panacea, but perhaps it will work for other libraries like jQuery – Oleg Mar 15 '14 at 13:27
  • @Teemu your solution doesn't work, unfortunately ( – Oleg Mar 15 '14 at 13:28
  • 1
    @Zub [How is that](http://jsfiddle.net/u96np/)? - I see, it doesn't work in Chrome, works fine in IE and FF though. – Teemu Mar 15 '14 at 13:30
  • @Teemu Yes, it works in FF and IE, but not Chrome (33.0.1750.117 - Linux) – Oleg Mar 15 '14 at 13:35
  • 3
    Why do you think it's a bug? DOM Level 2 defines "style" as a readonly **attribute** — http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-htmlelementcss Just because some browsers choose to implement style as getter/setter on `HTMLElement.prototype` and others — as direct property of an element object (`document.createElement('p').hasOwnProperty('style') === true` in Chrome) doesn't mean that it's a bug. It's just an implementation detail. – kangax Mar 16 '14 at 04:22
  • @kangax thank you for such deep explanation! So if Chrome implements `style` as direct property of an element, do I have to perform `Object.defineProperty` for **each** element on a page, or there is an easier way to disable styling? – Oleg Mar 16 '14 at 06:20
  • But… why? Why do you want to restrict access to anything? – bjb568 Mar 16 '14 at 06:57
  • @Zub Open documentation file. Put in a big, bold, red "DO NOT USE STYLE ATTRIBUTE" warning. :P – bjb568 Mar 16 '14 at 07:38
  • @bjb568 Yeah, I definitely have to do it as well :D – Oleg Mar 16 '14 at 07:41
  • 1
    @Zub Yeah, you'd need to seal each element individually. Or overwrite all of the methods (`createElement`, `getElementById`, etc.) that return an element to return already sealed element. But that's an even bigger can of worms, so I don't suggest you even go there. – kangax Mar 16 '14 at 15:26
  • @kangax Sadly. It looks like I have to follow bjb568's recommendation :) Thank you anyway :) – Oleg Mar 16 '14 at 17:25

1 Answers1

1

From the comments you can tell that restricting access to the style property is probably not such a good route. But I understand from your question that your reason from trying to go this route is that the resize event only fires for the window object.

If you would be able to circumvent the whole restricting issue with a resize event on just any element, than may I suggest you look into Ben Alman's jQuery resize event plugin. I'm not sure whether you want to develop with jQuery, but even if you don't it may be worth it to read that plugin's code. Basically, what it does is make a hashtable of listeners mapped to the element(s) they are listening on and then in a polling loop (with setTimeout or setInterval) check the size of the elements in that map. If it has changed during the interval (250ms by default) it triggers the listeners itself. It works reasonably well. That specific plugin hooks into jQuery's event system, but you could just make your own addResizeEvent function or something to that effect.

Edit: Upon re-reading your question it dawns on me that it looks like you are trying to develop some mechanism to deal with the downsides of the CSS box model, e.g. that when you give an element 5px padding and a width of 50% it will end up being 10 pixels wider than half the parent container. Consider box-sizing: border-box if that is the case.

Stijn de Witt
  • 40,192
  • 13
  • 79
  • 80
  • Yes, I thought about `setInterval`. But there is a problem with this approach. Lets say we have a situation where B-element's width depends on A-element's width. We change A width, and in 100ms we want to get B width. Since `setInterval` callback is being called with 250ms interval, we can't say whether B width has been updated by this time, or not. Sometimes we will get an old value, and sometimes new one. I bet that this "sometimes" stuff drives every programmer crazy :) That's why I decided not to use `setInterval` in this. – Oleg Mar 23 '14 at 07:05
  • Yes, if you would use setInterval, the `setWidth()` call in your API would need to just add an operation to some stack and on the polling loop you would need to process them in order. But what about `box-sizing: border-box`? It seems it can fix most of the situations where you would need to do things like `setWidth(parent.width / 2 -10)`. – Stijn de Witt Mar 23 '14 at 10:40
  • In case of `parent.width / 2 -10` it can. But if we have more complicated expression, e.g. `parent.width - #someEl.height + #someOtherEl.paddingLeft + 50`, it won't do the trick. Of course this is contrived example, but it demonstrates the flexibility of this kind of expressions – Oleg Mar 23 '14 at 10:54
  • What is your intention with the API? Which problem are you trying to solve? – Stijn de Witt Mar 23 '14 at 21:02
  • I want to create layout library that allows to write the above expressions. I wanted to forbid accessing to `style` attribute because as I mentioned it might break the elements behavior. But since I can't do it cross-browser, I will probably give up the idea with forbidding `style`, and search other ways to keep elements behavior in order. And I will accept your answer because I appreciate your effort. Thank you very much for your help :) – Oleg Mar 24 '14 at 06:15