提交 564548fa 编写于 作者: J Johannes Rieken

use native classlist when possible, fixes #25771

上级 e1b9f94c
......@@ -56,125 +56,133 @@ export function isInDOM(node: Node): boolean {
return false;
}
let lastStart: number, lastEnd: number;
const _manualClassList = new class {
function _findClassName(node: HTMLElement, className: string): void {
private _lastStart: number;
private _lastEnd: number;
let classes = node.className;
if (!classes) {
lastStart = -1;
return;
}
className = className.trim();
let classesLen = classes.length,
classLen = className.length;
if (classLen === 0) {
lastStart = -1;
return;
}
private _findClassName(node: HTMLElement, className: string): void {
if (classesLen < classLen) {
lastStart = -1;
return;
}
if (classes === className) {
lastStart = 0;
lastEnd = classesLen;
return;
}
let idx = -1,
idxEnd: number;
let classes = node.className;
if (!classes) {
this._lastStart = -1;
return;
}
while ((idx = classes.indexOf(className, idx + 1)) >= 0) {
className = className.trim();
idxEnd = idx + classLen;
let classesLen = classes.length,
classLen = className.length;
// a class that is followed by another class
if ((idx === 0 || classes.charCodeAt(idx - 1) === CharCode.Space) && classes.charCodeAt(idxEnd) === CharCode.Space) {
lastStart = idx;
lastEnd = idxEnd + 1;
if (classLen === 0) {
this._lastStart = -1;
return;
}
// last class
if (idx > 0 && classes.charCodeAt(idx - 1) === CharCode.Space && idxEnd === classesLen) {
lastStart = idx - 1;
lastEnd = idxEnd;
if (classesLen < classLen) {
this._lastStart = -1;
return;
}
// equal - duplicate of cmp above
if (idx === 0 && idxEnd === classesLen) {
lastStart = 0;
lastEnd = idxEnd;
if (classes === className) {
this._lastStart = 0;
this._lastEnd = classesLen;
return;
}
let idx = -1,
idxEnd: number;
while ((idx = classes.indexOf(className, idx + 1)) >= 0) {
idxEnd = idx + classLen;
// a class that is followed by another class
if ((idx === 0 || classes.charCodeAt(idx - 1) === CharCode.Space) && classes.charCodeAt(idxEnd) === CharCode.Space) {
this._lastStart = idx;
this._lastEnd = idxEnd + 1;
return;
}
// last class
if (idx > 0 && classes.charCodeAt(idx - 1) === CharCode.Space && idxEnd === classesLen) {
this._lastStart = idx - 1;
this._lastEnd = idxEnd;
return;
}
// equal - duplicate of cmp above
if (idx === 0 && idxEnd === classesLen) {
this._lastStart = 0;
this._lastEnd = idxEnd;
return;
}
}
this._lastStart = -1;
}
lastStart = -1;
}
hasClass(node: HTMLElement, className: string): boolean {
this._findClassName(node, className);
return this._lastStart !== -1;
}
/**
* @param node a dom node
* @param className a class name
* @return true if the className attribute of the provided node contains the provided className
*/
export function hasClass(node: HTMLElement, className: string): boolean {
_findClassName(node, className);
return lastStart !== -1;
}
addClass(node: HTMLElement, className: string): void {
if (!node.className) { // doesn't have it for sure
node.className = className;
} else {
this._findClassName(node, className); // see if it's already there
if (this._lastStart === -1) {
node.className = node.className + ' ' + className;
}
}
}
/**
* Adds the provided className to the provided node. This is a no-op
* if the class is already set.
* @param node a dom node
* @param className a class name
*/
export function addClass(node: HTMLElement, className: string): void {
if (!node.className) { // doesn't have it for sure
node.className = className;
} else {
_findClassName(node, className); // see if it's already there
if (lastStart === -1) {
node.className = node.className + ' ' + className;
removeClass(node: HTMLElement, className: string): void {
this._findClassName(node, className);
if (this._lastStart === -1) {
return; // Prevent styles invalidation if not necessary
} else {
node.className = node.className.substring(0, this._lastStart) + node.className.substring(this._lastEnd);
}
}
}
/**
* Removes the className for the provided node. This is a no-op
* if the class isn't present.
* @param node a dom node
* @param className a class name
*/
export function removeClass(node: HTMLElement, className: string): void {
_findClassName(node, className);
if (lastStart === -1) {
return; // Prevent styles invalidation if not necessary
} else {
node.className = node.className.substring(0, lastStart) + node.className.substring(lastEnd);
toggleClass(node: HTMLElement, className: string, shouldHaveIt?: boolean): void {
this._findClassName(node, className);
if (this._lastStart !== -1 && (shouldHaveIt === void 0 || !shouldHaveIt)) {
this.removeClass(node, className);
}
if (this._lastStart === -1 && (shouldHaveIt === void 0 || shouldHaveIt)) {
this.addClass(node, className);
}
}
}
};
/**
* @param node a dom node
* @param className a class name
* @param shouldHaveIt
*/
export function toggleClass(node: HTMLElement, className: string, shouldHaveIt?: boolean): void {
_findClassName(node, className);
if (lastStart !== -1 && (shouldHaveIt === void 0 || !shouldHaveIt)) {
removeClass(node, className);
const _nativeClassList = new class {
hasClass(node: HTMLElement, className: string): boolean {
return className && node.classList.contains(className);
}
if (lastStart === -1 && (shouldHaveIt === void 0 || shouldHaveIt)) {
addClass(node, className);
addClass(node: HTMLElement, className: string): void {
return className && node.classList.add(className);
}
}
removeClass(node: HTMLElement, className: string): void {
return className && node.classList.remove(className);
}
toggleClass(node: HTMLElement, className: string, shouldHaveIt?: boolean): void {
node.classList.toggle(className, shouldHaveIt);
}
};
// In IE11 there is only partial support for `classList` which makes us keep our
// custom implementation. Otherwise use the native implementation, see: http://caniuse.com/#search=classlist
const _classList = browser.isIE ? _manualClassList : _nativeClassList;
export const hasClass: (node: HTMLElement, className: string) => boolean = _classList.hasClass.bind(_classList);
export const addClass: (node: HTMLElement, className: string) => void = _classList.addClass.bind(_classList);
export const removeClass: (node: HTMLElement, className: string) => void = _classList.removeClass.bind(_classList);
export const toggleClass: (node: HTMLElement, className: string, shouldHaveIt?: boolean) => void = _classList.toggleClass.bind(_classList);
class DomListener implements IDisposable {
......@@ -1027,4 +1035,4 @@ export function domContentLoaded(): TPromise<any> {
window.addEventListener('DOMContentLoaded', c, false);
}
});
}
\ No newline at end of file
}
......@@ -58,7 +58,9 @@ suite('dom', () => {
test('removeClass should consider hyphens', function () {
let element = document.createElement('div');
dom.addClass(element, 'foo-bar bar');
dom.addClass(element, 'foo-bar');
dom.addClass(element, 'bar');
assert(dom.hasClass(element, 'foo-bar'));
assert(dom.hasClass(element, 'bar'));
......@@ -179,4 +181,4 @@ suite('dom', () => {
assert.equal(div.firstChild.textContent, 'hello');
});
});
});
\ No newline at end of file
});
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册