Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

fix(jqLite): make class API work consistently for IE9 #9510

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 65 additions & 3 deletions src/jqLite.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
/* global JQLitePrototype: true,
addEventListenerFn: true,
removeEventListenerFn: true,
jqLiteHasClass: true,
jqLiteAddClass: true,
jqLiteRemoveClass: true,
BOOLEAN_ATTR: true,
ALIASED_ATTR: true,
*/
Expand Down Expand Up @@ -351,13 +354,18 @@ function jqLiteData(element, key, value) {
}
}

function jqLiteHasClass(element, selector) {

var jqLiteHasClass = msie === 9 ? jqLiteHasClassIE9 : jqLiteHasClassCommon;
var jqLiteAddClass = msie === 9 ? jqLiteAddClassIE9 : jqLiteAddClassCommon;
var jqLiteRemoveClass = msie === 9 ? jqLiteRemoveClassIE9 : jqLiteRemoveClassCommon;

function jqLiteHasClassCommon(element, selector) {
if (!element.getAttribute) return false;
return ((" " + (element.getAttribute('class') || '') + " ").replace(/[\n\t]/g, " ").
indexOf( " " + selector + " " ) > -1);
}

function jqLiteRemoveClass(element, cssClasses) {
function jqLiteRemoveClassCommon(element, cssClasses) {
if (cssClasses && element.setAttribute) {
forEach(cssClasses.split(' '), function(cssClass) {
element.setAttribute('class', trim(
Expand All @@ -369,7 +377,7 @@ function jqLiteRemoveClass(element, cssClasses) {
}
}

function jqLiteAddClass(element, cssClasses) {
function jqLiteAddClassCommon(element, cssClasses) {
if (cssClasses && element.setAttribute) {
var existingClasses = (' ' + (element.getAttribute('class') || '') + ' ')
.replace(/[\n\t]/g, " ");
Expand All @@ -385,6 +393,60 @@ function jqLiteAddClass(element, cssClasses) {
}
}

function jqLiteHasClassIE9(element, selector) {
if (!element.className && element.className !== '') return false;
var existingClasses = (' ' + (element.className.baseVal || element.className || '') + ' ')
.replace(/[\r\n\s]+/g, ' ');
return existingClasses.indexOf( " " + selector + " " ) > -1;
}

function jqLiteRemoveClassIE9(element, cssClasses) {
if (cssClasses && (element.className || element.className === '')) {
var existingClasses = (' ' + (element.className.baseVal || element.className || '') + ' ')
.replace(/[\r\n\s]+/g, ' ');

forEach(cssClasses.split(' '), function(cssClass) {
cssClass = trim(cssClass);
existingClasses = existingClasses.replace(' ' + cssClass + ' ', ' ');
});

existingClasses = trim(existingClasses);

if (typeof element.className === 'string') {
/* assuming regular node */
element.className = existingClasses;
} else {
/* assuming SVG */
element.className.baseVal = existingClasses;
}
}
}

function jqLiteAddClassIE9(element, cssClasses) {
// MSIE9 cannot reliably use setAttribute() when XML namespaces are used anywhere
// on the element. Because of this, we work with className directly.
if (cssClasses && (element.className || element.className === '')) {
var existingClasses = (' ' + (element.className.baseVal || element.className || '') + ' ')
.replace(/[\r\n\s]+/g, ' ');

forEach(cssClasses.split(' '), function(cssClass) {
cssClass = trim(cssClass);
if (existingClasses.indexOf(' ' + cssClass + ' ') === -1) {
existingClasses += cssClass + ' ';
}
});
existingClasses = trim(existingClasses);

if (typeof element.className === 'string') {
/* assuming regular node */
element.className = existingClasses;
} else {
/* assuming SVG */
element.className.baseVal = existingClasses;
}
}
}


function jqLiteAddNodes(root, elements) {
// THIS CODE IS VERY HOT. Don't make changes without benchmarking.
Expand Down
31 changes: 31 additions & 0 deletions test/jqLiteSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -663,6 +663,37 @@ describe('jqLite', function() {
});


it('should properly work with elements with XML namespaced attributes', function() {
var element = jqLite('<div ng:class="foo bar baz"></div>');

element.addClass('foo');
element.addClass('bar');
expect(element.hasClass('foo')).toBe(true);
expect(element.hasClass('bar')).toBe(true);

element.removeClass('foo');
expect(element.hasClass('foo')).toBe(false);
expect(element.hasClass('bar')).toBe(true);
});


it('should properly work with elements with XML namespaced attributes for SVG', 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;
var svg = jqLite('<svg><rect ng:class="foo bar baz"></rect></svg>');
var element = svg.children();

element.addClass('foo');
element.addClass('bar');
expect(element.hasClass('foo')).toBe(true);
expect(element.hasClass('bar')).toBe(true);

element.removeClass('foo');
expect(element.hasClass('foo')).toBe(false);
expect(element.hasClass('bar')).toBe(true);
});


it('should ignore comment elements', function() {
var comment = jqLite(document.createComment('something'));

Expand Down