diff --git a/src/.jshintrc b/src/.jshintrc index fc2ae7403..70c481c30 100644 --- a/src/.jshintrc +++ b/src/.jshintrc @@ -143,8 +143,6 @@ "getAliasedAttrName": false, "createEventHandler": false, "JQLitePrototype": false, - "addEventListenerFn": false, - "removeEventListenerFn": false, "jqLiteIsTextNode": false, "jqLiteDocumentLoaded": false, diff --git a/src/jqLite.js b/src/jqLite.js index da55bae86..59219ae6a 100644 --- a/src/jqLite.js +++ b/src/jqLite.js @@ -12,8 +12,6 @@ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* global JQLitePrototype: true, - addEventListenerFn: true, - removeEventListenerFn: true, BOOLEAN_ATTR: true, ALIASED_ATTR: true, */ @@ -121,13 +119,7 @@ JQLite.expando = 'ng339'; var jqCache = JQLite.cache = {}, - jqId = 1, - addEventListenerFn = function(element, type, fn) { - element.addEventListener(type, fn, false); - }, - removeEventListenerFn = function(element, type, fn) { - element.removeEventListener(type, fn, false); - }; + jqId = 1; /* * !!! This is an undocumented "private" function !!! @@ -325,7 +317,7 @@ function jqLiteOff(element, type, fn, unsupported) { if (!type) { for (type in events) { if (type !== '$destroy') { - removeEventListenerFn(element, type, handle); + element.removeEventListener(type, handle); } delete events[type]; } @@ -337,7 +329,7 @@ function jqLiteOff(element, type, fn, unsupported) { arrayRemove(listenerFns || [], fn); } if (!(isDefined(fn) && listenerFns && listenerFns.length > 0)) { - removeEventListenerFn(element, type, handle); + element.removeEventListener(type, handle); delete events[type]; } }; @@ -874,7 +866,7 @@ forEach({ eventFns = events[type] = []; eventFns.specialHandlerWrapper = specialHandlerWrapper; if (type !== '$destroy' && !noEventListener) { - addEventListenerFn(element, type, handle); + element.addEventListener(type, handle); } } diff --git a/src/ng/directive/form.js b/src/ng/directive/form.js index 30a932a46..d88b02c55 100644 --- a/src/ng/directive/form.js +++ b/src/ng/directive/form.js @@ -497,13 +497,13 @@ var formDirectiveFactory = function(isNgForm) { event.preventDefault(); }; - addEventListenerFn(formElement[0], 'submit', handleFormSubmission); + formElement[0].addEventListener('submit', handleFormSubmission); // unregister the preventDefault listener so that we don't not leak memory but in a // way that will achieve the prevention of the default action. formElement.on('$destroy', function() { $timeout(function() { - removeEventListenerFn(formElement[0], 'submit', handleFormSubmission); + formElement[0].removeEventListener('submit', handleFormSubmission); }, 0, false); }); } diff --git a/src/ng/httpBackend.js b/src/ng/httpBackend.js index 0b16b34a7..6d17fd44f 100644 --- a/src/ng/httpBackend.js +++ b/src/ng/httpBackend.js @@ -172,8 +172,8 @@ function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDoc script.async = true; callback = function(event) { - removeEventListenerFn(script, "load", callback); - removeEventListenerFn(script, "error", callback); + script.removeEventListener('load', callback); + script.removeEventListener('error', callback); rawDocument.body.removeChild(script); script = null; var status = -1; @@ -192,8 +192,8 @@ function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDoc } }; - addEventListenerFn(script, "load", callback); - addEventListenerFn(script, "error", callback); + script.addEventListener('load', callback); + script.addEventListener('error', callback); rawDocument.body.appendChild(script); return callback; } diff --git a/test/.jshintrc b/test/.jshintrc index ac2419547..f7749ef4e 100644 --- a/test/.jshintrc +++ b/test/.jshintrc @@ -117,8 +117,6 @@ "getBooleanAttrName": false, "createEventHandler": false, "JQLitePrototype": false, - "addEventListenerFn": false, - "removeEventListenerFn": false, "jqLiteDocumentLoaded": false, /* apis.js */ diff --git a/test/jqLiteSpec.js b/test/jqLiteSpec.js index 39216912f..28e721fc1 100644 --- a/test/jqLiteSpec.js +++ b/test/jqLiteSpec.js @@ -711,8 +711,12 @@ describe('jqLite', function() { describe('class', function() { it('should properly do with SVG elements', function() { - // this is a jqLite & SVG only test (jquery doesn't behave this way right now, which is a bug) - if (!window.SVGElement || !_jqLiteMode) return; + // This is not working correctly in jQuery prior to v3.0. + // See https://github.com/jquery/jquery/issues/2199 for details. + var jQueryVersion = window.jQuery && window.jQuery.fn.jquery.split('.')[0]; + var jQuery3xOrNewer = jQueryVersion && (Number(jQueryVersion) >= 3); + if (!_jqLiteMode && !jQuery3xOrNewer) return; + var svg = jqLite(''); var rect = svg.children(); @@ -1536,6 +1540,10 @@ describe('jqLite', function() { describe('native listener deregistration', function() { + var jQueryVersionString = window.jQuery && window.jQuery.fn.jquery; + var jQueryMajor = jQueryVersionString && Number(jQueryVersionString.split('.')[0]); + var jQueryMinor = jQueryVersionString && Number(jQueryVersionString.split('.')[1]); + var jQuery21 = jQueryMajor === 2 && jQueryMinor === 1; it('should deregister the native listener when all jqLite listeners for given type are gone ' + 'after off("eventName", listener) call', function() { @@ -1547,12 +1555,22 @@ describe('jqLite', function() { var jqLiteListener = function() {}; aElem.on('click', jqLiteListener); - expect(addEventListenerSpy).toHaveBeenCalledOnceWith('click', jasmine.any(Function), false); + // jQuery <2.2 passes the non-needed `false` useCapture parameter. + // See https://github.com/jquery/jquery/issues/2199 for details. + if (jQuery21) { + expect(addEventListenerSpy).toHaveBeenCalledOnceWith('click', jasmine.any(Function), false); + } else { + expect(addEventListenerSpy).toHaveBeenCalledOnceWith('click', jasmine.any(Function)); + } nativeListenerFn = addEventListenerSpy.calls.mostRecent().args[1]; expect(removeEventListenerSpy).not.toHaveBeenCalled(); aElem.off('click', jqLiteListener); - expect(removeEventListenerSpy).toHaveBeenCalledOnceWith('click', nativeListenerFn, false); + if (jQuery21) { + expect(removeEventListenerSpy).toHaveBeenCalledOnceWith('click', nativeListenerFn, false); + } else { + expect(removeEventListenerSpy).toHaveBeenCalledOnceWith('click', nativeListenerFn); + } }); @@ -1564,12 +1582,20 @@ describe('jqLite', function() { var nativeListenerFn; aElem.on('click', function() {}); - expect(addEventListenerSpy).toHaveBeenCalledOnceWith('click', jasmine.any(Function), false); + if (jQuery21) { + expect(addEventListenerSpy).toHaveBeenCalledOnceWith('click', jasmine.any(Function), false); + } else { + expect(addEventListenerSpy).toHaveBeenCalledOnceWith('click', jasmine.any(Function)); + } nativeListenerFn = addEventListenerSpy.calls.mostRecent().args[1]; expect(removeEventListenerSpy).not.toHaveBeenCalled(); aElem.off('click'); - expect(removeEventListenerSpy).toHaveBeenCalledOnceWith('click', nativeListenerFn, false); + if (jQuery21) { + expect(removeEventListenerSpy).toHaveBeenCalledOnceWith('click', nativeListenerFn, false); + } else { + expect(removeEventListenerSpy).toHaveBeenCalledOnceWith('click', nativeListenerFn); + } }); @@ -1581,19 +1607,32 @@ describe('jqLite', function() { var nativeListenerFn; aElem.on('click', function() {}); - expect(addEventListenerSpy).toHaveBeenCalledOnceWith('click', jasmine.any(Function), false); + if (jQuery21) { + expect(addEventListenerSpy).toHaveBeenCalledOnceWith('click', jasmine.any(Function), false); + } else { + expect(addEventListenerSpy).toHaveBeenCalledOnceWith('click', jasmine.any(Function)); + } nativeListenerFn = addEventListenerSpy.calls.mostRecent().args[1]; addEventListenerSpy.calls.reset(); aElem.on('dblclick', function() {}); - expect(addEventListenerSpy).toHaveBeenCalledOnceWith('dblclick', nativeListenerFn, false); + if (jQuery21) { + expect(addEventListenerSpy).toHaveBeenCalledOnceWith('dblclick', nativeListenerFn, false); + } else { + expect(addEventListenerSpy).toHaveBeenCalledOnceWith('dblclick', nativeListenerFn); + } expect(removeEventListenerSpy).not.toHaveBeenCalled(); aElem.off('click dblclick'); - expect(removeEventListenerSpy).toHaveBeenCalledWith('click', nativeListenerFn, false); - expect(removeEventListenerSpy).toHaveBeenCalledWith('dblclick', nativeListenerFn, false); + if (jQuery21) { + expect(removeEventListenerSpy).toHaveBeenCalledWith('click', nativeListenerFn, false); + expect(removeEventListenerSpy).toHaveBeenCalledWith('dblclick', nativeListenerFn, false); + } else { + expect(removeEventListenerSpy).toHaveBeenCalledWith('click', nativeListenerFn); + expect(removeEventListenerSpy).toHaveBeenCalledWith('dblclick', nativeListenerFn); + } expect(removeEventListenerSpy).toHaveBeenCalledTimes(2); }); @@ -1606,17 +1645,30 @@ describe('jqLite', function() { var nativeListenerFn; aElem.on('click', function() {}); - expect(addEventListenerSpy).toHaveBeenCalledOnceWith('click', jasmine.any(Function), false); + if (jQuery21) { + expect(addEventListenerSpy).toHaveBeenCalledOnceWith('click', jasmine.any(Function), false); + } else { + expect(addEventListenerSpy).toHaveBeenCalledOnceWith('click', jasmine.any(Function)); + } nativeListenerFn = addEventListenerSpy.calls.mostRecent().args[1]; addEventListenerSpy.calls.reset(); aElem.on('dblclick', function() {}); - expect(addEventListenerSpy).toHaveBeenCalledOnceWith('dblclick', nativeListenerFn, false); + if (jQuery21) { + expect(addEventListenerSpy).toHaveBeenCalledOnceWith('dblclick', nativeListenerFn, false); + } else { + expect(addEventListenerSpy).toHaveBeenCalledOnceWith('dblclick', nativeListenerFn); + } aElem.off(); - expect(removeEventListenerSpy).toHaveBeenCalledWith('click', nativeListenerFn, false); - expect(removeEventListenerSpy).toHaveBeenCalledWith('dblclick', nativeListenerFn, false); + if (jQuery21) { + expect(removeEventListenerSpy).toHaveBeenCalledWith('click', nativeListenerFn, false); + expect(removeEventListenerSpy).toHaveBeenCalledWith('dblclick', nativeListenerFn, false); + } else { + expect(removeEventListenerSpy).toHaveBeenCalledWith('click', nativeListenerFn); + expect(removeEventListenerSpy).toHaveBeenCalledWith('dblclick', nativeListenerFn); + } expect(removeEventListenerSpy).toHaveBeenCalledTimes(2); }); }); diff --git a/test/ng/directive/formSpec.js b/test/ng/directive/formSpec.js index 56ac114ac..eecce5a5e 100644 --- a/test/ng/directive/formSpec.js +++ b/test/ng/directive/formSpec.js @@ -397,7 +397,7 @@ describe('form', function() { submitted = true; }; - addEventListenerFn(doc[0], 'submit', assertPreventDefaultListener); + doc[0].addEventListener('submit', assertPreventDefaultListener); browserTrigger(doc.find('input')); @@ -409,7 +409,7 @@ describe('form', function() { expect(submitted).toBe(true); // prevent mem leak in test - removeEventListenerFn(doc[0], 'submit', assertPreventDefaultListener); + doc[0].removeEventListener('submit', assertPreventDefaultListener); }) .done(); job.start(); @@ -448,7 +448,7 @@ describe('form', function() { $compile(doc)(scope); - addEventListenerFn(form[0], 'submit', assertPreventDefaultListener); + form[0].addEventListener('submit', assertPreventDefaultListener); browserTrigger(doc.find('button'), 'click'); @@ -467,7 +467,7 @@ describe('form', function() { // now. (i) // prevent mem leak in test - removeEventListenerFn(form[0], 'submit', assertPreventDefaultListener); + form[0].removeEventListener('submit', assertPreventDefaultListener); }) .done(); job.start();