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

perf(jqLite): avoid repeated add/removeAttribute in jqLiteRemoveClass #16131

Merged
merged 2 commits into from
Sep 17, 2017
Merged
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
24 changes: 16 additions & 8 deletions src/jqLite.js
Original file line number Diff line number Diff line change
Expand Up @@ -420,29 +420,37 @@ function jqLiteHasClass(element, selector) {

function jqLiteRemoveClass(element, cssClasses) {
if (cssClasses && element.setAttribute) {
var existingClasses = (' ' + (element.getAttribute('class') || '') + ' ')
.replace(/[\n\t]/g, ' ');
var newClasses = existingClasses;

forEach(cssClasses.split(' '), function(cssClass) {
element.setAttribute('class', trim(
(' ' + (element.getAttribute('class') || '') + ' ')
.replace(/[\n\t]/g, ' ')
.replace(' ' + trim(cssClass) + ' ', ' '))
);
cssClass = trim(cssClass);
newClasses = newClasses.replace(' ' + cssClass + ' ', ' ');
});

if (newClasses !== existingClasses) {
element.setAttribute('class', trim(newClasses));
}
}
}

function jqLiteAddClass(element, cssClasses) {
if (cssClasses && element.setAttribute) {
var existingClasses = (' ' + (element.getAttribute('class') || '') + ' ')
.replace(/[\n\t]/g, ' ');
var newClasses = existingClasses;

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

element.setAttribute('class', trim(existingClasses));
if (newClasses !== existingClasses) {
element.setAttribute('class', trim(newClasses));
}
}
}

Expand Down
62 changes: 62 additions & 0 deletions test/jqLiteSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -914,6 +914,37 @@ describe('jqLite', function() {
});


// JQLite specific implementation/performance tests
if (_jqLiteMode) {
it('should only get/set the attribute once when multiple classes added', function() {
var fakeElement = {
nodeType: 1,
setAttribute: jasmine.createSpy(),
getAttribute: jasmine.createSpy().and.returnValue('')
};
var jqA = jqLite(fakeElement);

jqA.addClass('foo bar baz');
expect(fakeElement.getAttribute).toHaveBeenCalledOnceWith('class');
expect(fakeElement.setAttribute).toHaveBeenCalledOnceWith('class', 'foo bar baz');
});


it('should not set the attribute when classes not changed', function() {
var fakeElement = {
nodeType: 1,
setAttribute: jasmine.createSpy(),
getAttribute: jasmine.createSpy().and.returnValue('foo bar')
};
var jqA = jqLite(fakeElement);

jqA.addClass('foo');
expect(fakeElement.getAttribute).toHaveBeenCalledOnceWith('class');
expect(fakeElement.setAttribute).not.toHaveBeenCalled();
});
}


it('should not add duplicate classes', function() {
var jqA = jqLite(a);
expect(a.className).toBe('');
Expand Down Expand Up @@ -1031,6 +1062,37 @@ describe('jqLite', function() {
jqA.removeClass('foo baz noexistent');
expect(a.className).toBe('bar');
});


// JQLite specific implementation/performance tests
if (_jqLiteMode) {
it('should get/set the attribute once when removing multiple classes', function() {
var fakeElement = {
nodeType: 1,
setAttribute: jasmine.createSpy(),
getAttribute: jasmine.createSpy().and.returnValue('foo bar baz')
};
var jqA = jqLite(fakeElement);

jqA.removeClass('foo baz noexistent');
expect(fakeElement.getAttribute).toHaveBeenCalledOnceWith('class');
expect(fakeElement.setAttribute).toHaveBeenCalledOnceWith('class', 'bar');
});


it('should not set the attribute when classes not changed', function() {
var fakeElement = {
nodeType: 1,
setAttribute: jasmine.createSpy(),
getAttribute: jasmine.createSpy().and.returnValue('foo bar')
};
var jqA = jqLite(fakeElement);

jqA.removeClass('noexistent');
expect(fakeElement.getAttribute).toHaveBeenCalledOnceWith('class');
expect(fakeElement.setAttribute).not.toHaveBeenCalled();
});
}
});
});

Expand Down