4

Is there any approach or workaround to make a (global, if you wish, but not necessarily) function only available to inline event handlers of DOM elements?

JavaScript

function forInlineCallsOnly() {
    // only callable from inline "on" attributes of DOM elements
};

That is, the above function should not be able to be called from other parts of the script, only from, for example:

HTML

<a onclick="forInlineCallsOnly();">Click me</a>

Could this be possible using stack tracing?


Note: this is not a question of best-practices regarding where event handlers should be defined.

John Weisz
  • 30,137
  • 13
  • 89
  • 132
  • 2
    Can't you just _not_ call the function from elsewhere in the code? What's the problem you're trying to solve? – Andy May 22 '15 at 13:25
  • 1
    **No** you can't **restrict** function calling like so. Function scope can be available from any where. Question is now that, what you want to achieve? – Manwal May 22 '15 at 13:26
  • Pretty much what the title is about. It's just a small bootleg API only intended for use from inline elements. – John Weisz May 22 '15 at 13:28
  • 1
    Given that if you're using proper separation of concerns, there should be less and less javascript being put inline in attributes, this seems a little strange. The "norm" these days would be to assign a click handler well away from the actual DOM. – James Thorpe May 22 '15 at 13:30
  • @JamesThorpe You are right, these are required for select uncommon special cases. – John Weisz May 22 '15 at 13:32
  • you can check `forInlineCallsOnly.caller` but it's same problem as the other posted answer: detecting is one thing.. enforcing it is impossible. – CrayonViolent May 22 '15 at 13:35
  • Not to mention completely non-standard according to the MDN notes. – Andy May 22 '15 at 13:36
  • @Manwal Bold call, but how about extending the constructor of `document.body`? Wouldn't that, in theory, allow inline access to an object within the closure of that constructor? – John Weisz May 22 '15 at 13:42
  • Why do you want it to be inline? Why don't you just use JavaScript and create an event listener on the DOM elements? That is pretty much the standard way of doing it. – Hoyen May 22 '15 at 13:43
  • @Hoyen That is already taken care of. I need a simple and light API for use within inline event handlers. Rare, special cases, but they are relevant from a design perspective. – John Weisz May 22 '15 at 13:44
  • `this` inside the function will be the element. just check if that element has `onclick` attribute and if your function name is in there. that's as closest as you'll get. (that can also be faked, ofcourse) – yoavmatchulsky May 22 '15 at 13:49

2 Answers2

2

Best answer I can give; and heck, it's not great.

<a data-onclick-attach="forInlineCallsOnly">

JS

eventMethodsMap: {
  forInlineCallsOnly: function() {
    ...
  }
}
Array.prototype.forEach.call(document.querySelectorAll('[data-onclick-attach]'), function(elem) {
  elem.onclick = eventMethodsMap[elem.dataset.onclickAttach];
});

This is approximately how systems like Dojo do it; events can be defined in the HTML as data attributes, but they are hooked up in a more custom way. If you don't want to be onclick-specific, you could come up with a better query selector.

EDIT: You may also want to trim parentheses out of the onClickAttach attribute, in case people start treating it the same way as onclick.

Katana314
  • 8,429
  • 2
  • 28
  • 36
  • 1
    Even better if you wrap all your JS in an IIFE so that `eventMethodsMap.forInlineCallsOnly` doesn't leak to the global scope – James Thorpe May 22 '15 at 14:30
  • 1
    @JamesThorpe Hah, yup, of course. I also didn't put this in a "document-ready" listener, which you'd need; I simply assumed the OP could insert it somewhere in one of his own IIFEs. – Katana314 May 22 '15 at 14:34
  • Yes that's true - I hadn't clocked it wouldn't be able to be run inline on page load :) – James Thorpe May 22 '15 at 14:35
-1

Use the isElement function from: JavaScript isDOM -- How do you check if a JavaScript Object is a DOM Object?

And update your code to:

HTML:

<a onclick="forInlineCallsOnly(this);">Click me</a>

JAVASCRIPT:

function forInlineCallsOnly(obj) {
    // only callable from inline "on" attributes of DOM elements
    if(!isElement(obj)){
        return;
    }
    // Put code you want to allow to run here
};
Community
  • 1
  • 1
Hoyen
  • 2,511
  • 1
  • 12
  • 13
  • 2
    `forInlineCallsOnly(document.getElementById('someElement'));`? – James Thorpe May 22 '15 at 13:28
  • also, what's to stop someone from just redefining the function? – CrayonViolent May 22 '15 at 13:28
  • This is still _calling_ the function tho. It was my first thought too, but it doesn't solve the problem, merely offer a workaround. There's still the issue of what the OP _wants_ solve and why it's a problem. – Andy May 22 '15 at 13:29
  • 1
    That's not the point. There is no way to stop people from redefining functions in JavaScript. You can check if the object is a element or not and if it is, allow the function to execute. We aren't righting code to stop hackers and such – Hoyen May 22 '15 at 13:30
  • @Hoyen but it sounds like that's exactly what OP wants.. he said he wants to detect and enforce it – CrayonViolent May 22 '15 at 13:33
  • Also, since this is a plug-in seemingly for 3rd-party users, what if they use the function on an element and forget to add the `this` argument. It would fail. – Andy May 22 '15 at 13:35