Skip to content

Commit

Permalink
Add: support for inline event handlers (ie: <div onclick='some.horrib…
Browse files Browse the repository at this point in the history
…le.string()'>) (Brian McDaniel)
  • Loading branch information
tmpvar committed Sep 2, 2011
2 parents 9565feb + 6bd539a commit ba63584
Show file tree
Hide file tree
Showing 3 changed files with 117 additions and 1 deletion.
21 changes: 21 additions & 0 deletions lib/jsdom/level1/core.js
Original file line number Diff line number Diff line change
Expand Up @@ -955,6 +955,27 @@ core.Element.prototype = {

/* returns string */
setAttribute: function(/* string */ name, /* string */ value) {
// Check for inline event handlers.
// We can't set these like other attributes then look it up in
// dispatchEvent() because that would create 2 'traditional' event handlers
// in the case where there's an inline event handler attribute, plus one
// set using element.on* in a script.
if ((name.length > 2) && (name[0] == 'o') && (name[1] == 'n')) {
var self = this;
self[name] = function () {
// The handler code probably refers to functions declared in the
// window context, so we need to call run().
if (self.run != undefined) {
// We're the window. This can happen because inline handlers
// on the body are proxied to the window.
self.run(value);
} else {
// We're an element.
self._ownerDocument.parentWindow.run(value);
}
};
return;
}
if (this._ownerDocument) {
var attr = this._ownerDocument.createAttribute(name);
attr.value = value;
Expand Down
17 changes: 17 additions & 0 deletions lib/jsdom/level2/html.js
Original file line number Diff line number Diff line change
Expand Up @@ -626,6 +626,23 @@ define('HTMLStyleElement', {
});

define('HTMLBodyElement', {
init : function () {
// The body element's "traditional" event handlers are proxied to the
// window object.
// See: http://dev.w3.org/html5/spec/Overview.html#the-body-element
var self = this;
['onafterprint', 'onbeforeprint', 'onbeforeunload', 'onblur', 'onerror',
'onfocus', 'onhashchange', 'onload', 'onmessage', 'onoffline', 'ononline',
'onpagehide', 'onpageshow', 'onpopstate', 'onresize', 'onscroll',
'onstorage', 'onunload'].forEach(function (name) {
self.__defineSetter__(name, function (handler) {
self._ownerDocument.parentWindow[name] = handler;
});
self.__defineGetter__(name, function () {
return self._ownerDocument.parentWindow[name];
});
});
},
tagName: 'BODY',
attributes: [
'aLink',
Expand Down
80 changes: 79 additions & 1 deletion test/jsdom/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ exports.tests = {
jsdom.env({
html: html,
done: function(errors, window) {
test.equal(errors, null, 'errors should be null');
test.equal(errors, null, 'errors should be null');
test.notEqual(window.location, null, 'window.location should not be null');
test.done();
}
Expand Down Expand Up @@ -864,5 +864,83 @@ document.write("<SCR"+"IPT TYPE=\'text/javascript\' SRC=\'...\'><\/SCR"+"IPT>");
test.ok(doc.errors[0].message = "invalid markup");
test.ok(thrown === false);
test.done();
},

// Test inline event handlers set on the body.
test_body_event_handler_inline : function (test) {
var html = "\
<html>\
<head>\
<script>\
function loader () {\
window.loader_called = true;\
}\
</script>\
</head>\
<body onload='loader()'></body>\
</html>";
var doc = jsdom.jsdom(html, null, { deferClose : true });
var window = doc.parentWindow;
// In JSDOM, listeners registered with addEventListener are called before
// "traditional" listeners, so listening for 'load' will fire before our
// inline listener. This means we have to check the value on the next
// tick.
window.addEventListener('load', function () {
process.nextTick(function () {
test.equal(window.loader_called, true);
test.done();
});
});
doc.close();
},

// Make sure traditional handlers on the body element set via script are
// forwarded to the window.
test_body_event_handler_script : function (test) {
test.expect(2);
var doc = jsdom.jsdom("<html><head></head><body></body></html>",
null,
{deferClose : true});
var window = doc.parentWindow;
test.equal(window.onload, undefined);
doc.body.onload = function () {
test.done();
};
test.notEqual(window.onload, undefined);
doc.close();
},

// Test inline event handlers on a regular element.
test_element_inline_event_handler : function (test) {
var doc = jsdom.jsdom("\
<html>\
<head></head>\
<body>\
<div id='div1' onclick='window.divClicked = true;'\
onmouseover='window.divMousedOver = true;'\
</div>\
</body>\
</html>");
var window = doc.parentWindow;
var click = doc.createEvent('MouseEvents');
click.initMouseEvent('click', false, false);
var div = doc.getElementById('div1');
div.dispatchEvent(click);
var mouseOver = doc.createEvent('MouseEvents');
mouseOver.initMouseEvent('mouseover', false, false);
div.dispatchEvent(mouseOver);
test.equal(window.divClicked, true);
test.equal(window.divMousedOver, true);
test.done();
},

// Test for issue 287 - element.onevent check doesn't work
// See: https://github.com/tmpvar/jsdom/issues/287
issue_287 : function (test) {
var doc = jsdom.jsdom();
var elem = doc.createElement('form');
elem.setAttribute('onsubmit', ';');
test.equal(typeof elem.onsubmit, 'function');
test.done();
}
};

0 comments on commit ba63584

Please sign in to comment.