Skip to content

Commit 6be62e8

Browse files
committed
feat(core utils): removeWildcardClass: Add support for pure DOM nodes instead of needing jQuery objects.
1 parent 23336b9 commit 6be62e8

File tree

2 files changed

+107
-41
lines changed

2 files changed

+107
-41
lines changed

src/core/utils.js

Lines changed: 32 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -204,21 +204,40 @@ function escapeRegExp(str) {
204204
return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
205205
}
206206

207-
function removeWildcardClass($targets, classes) {
208-
if (classes.indexOf("*") === -1) $targets.removeClass(classes);
209-
else {
210-
var matcher = classes.replace(/[\-\[\]{}()+?.,\\\^$|#\s]/g, "\\$&");
207+
/**
208+
* Remove classes from a list of targets if they match a specific pattern.
209+
*
210+
* @param {Node, NodeList} targets: Dom Node or NodeList where the classes should be removed.
211+
* @param {string} classes: String matching classes to be removed.
212+
* You can add a "*" as wildcard to search for classes to be removed.
213+
* E.g. "icon-*-alert" to remove any of "icon-1-alert icon-2-alert".
214+
*
215+
* @returns {undefined}: This method directly operates on the targets.
216+
*/
217+
function removeWildcardClass(targets, classes) {
218+
targets = utils.ensureArray(targets);
219+
220+
if (classes.indexOf("*") === -1) {
221+
for (const target of targets) {
222+
target.classList.remove(classes);
223+
}
224+
} else {
225+
let matcher = classes.replace(/[\-\[\]{}()+?.,\\\^$|#\s]/g, "\\$&");
211226
matcher = matcher.replace(/[*]/g, ".*");
212227
matcher = new RegExp("^" + matcher + "$");
213-
$targets.filter("[class]").each(function () {
214-
var $this = $(this),
215-
classes = $this.attr("class").split(/\s+/),
216-
ok = [];
217-
for (var i = 0; i < classes.length; i++)
218-
if (!matcher.test(classes[i])) ok.push(classes[i]);
219-
if (ok.length) $this.attr("class", ok.join(" "));
220-
else $this.removeAttr("class");
221-
});
228+
229+
for (const target of targets) {
230+
const class_list = (target.getAttribute("class") || "").split(/\s+/);
231+
if (!class_list.length) {
232+
continue;
233+
}
234+
const ok = class_list.filter((it) => !matcher.test(it));
235+
if (ok.length) {
236+
target.setAttribute("class", ok.join(" "));
237+
} else {
238+
target.removeAttribute("class");
239+
}
240+
}
222241
}
223242
}
224243

src/core/utils.test.js

Lines changed: 75 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -214,40 +214,87 @@ describe("basic tests", function () {
214214
});
215215

216216
describe("removeWildcardClass", function () {
217-
it("Remove basic class", function () {
218-
var $el = $("<div class='on'/>");
219-
utils.removeWildcardClass($el, "on");
220-
expect($el.hasClass("on")).toBe(false);
221-
});
217+
describe("... with single element", function () {
218+
it("Remove basic class", function () {
219+
const el = document.createElement("div");
220+
el.classList.add("on");
221+
utils.removeWildcardClass(el, "on");
222+
expect(el.classList.contains("on")).toBe(false);
223+
});
222224

223-
it("Keep other classes", function () {
224-
var $el = $("<div class='one two'/>");
225-
utils.removeWildcardClass($el, "one");
226-
expect($el.attr("class")).toBe("two");
227-
});
225+
it("Keep other classes", function () {
226+
const el = document.createElement("div");
227+
el.classList.add("one");
228+
el.classList.add("two");
229+
utils.removeWildcardClass(el, "one");
230+
expect(el.classList.contains("one")).toBe(false);
231+
expect(el.classList.contains("two")).toBe(true);
232+
});
228233

229-
it("Remove uses whole words", function () {
230-
var $el = $("<div class='cheese-on-bread'/>");
231-
utils.removeWildcardClass($el, "on");
232-
expect($el.attr("class")).toBe("cheese-on-bread");
233-
});
234+
it("Remove removes whole words", function () {
235+
const el = document.createElement("div");
236+
el.classList.add("cheese-on-bread");
237+
utils.removeWildcardClass(el, "on");
238+
expect(el.getAttribute("class")).toBe("cheese-on-bread");
239+
});
234240

235-
it("Remove wildcard postfix class", function () {
236-
var $el = $("<div class='icon-small'/>");
237-
utils.removeWildcardClass($el, "icon-*");
238-
expect($el.attr("class")).toBeFalsy();
239-
});
241+
it("Remove wildcard postfix class", function () {
242+
const el = document.createElement("div");
243+
el.classList.add("icon-small");
244+
utils.removeWildcardClass(el, "icon-*");
245+
expect(el.getAttribute("class")).toBeFalsy();
246+
});
247+
248+
it("Remove wildcard infix class", function () {
249+
const el = document.createElement("div");
250+
el.classList.add("icon-small-alert");
251+
utils.removeWildcardClass(el, "icon-*-alert");
252+
expect(el.getAttribute("class")).toBeFalsy();
253+
});
240254

241-
it("Remove wildcard infix class", function () {
242-
var $el = $("<div class='icon-small-alert/>");
243-
utils.removeWildcardClass($el, "icon-*-alert");
244-
expect($el.attr("class")).toBeFalsy();
255+
it("Keep other classes when removing wildcards", function () {
256+
const el = document.createElement("div");
257+
el.classList.add("icon-small");
258+
el.classList.add("foo");
259+
utils.removeWildcardClass(el, "icon-*");
260+
expect(el.getAttribute("class")).toBe("foo");
261+
});
245262
});
246263

247-
it("Keep other classes when removing wildcards", function () {
248-
var $el = $("<div class='icon-small foo'/>");
249-
utils.removeWildcardClass($el, "icon-*");
250-
expect($el.attr("class")).toBe("foo");
264+
describe("... with a list of elements", function () {
265+
beforeEach(function () {
266+
document.body.innerHTML = "";
267+
});
268+
269+
afterEach(function () {
270+
document.body.innerHTML = "";
271+
});
272+
273+
it("Keep other classes", function () {
274+
document.body.innerHTML = `
275+
<div class="one two"></div>
276+
<div class="two three"></div>
277+
`;
278+
const inner_els = document.querySelectorAll("div");
279+
utils.removeWildcardClass(inner_els, "two");
280+
expect(inner_els[0].classList.contains("one")).toBe(true);
281+
expect(inner_els[0].classList.contains("two")).toBe(false);
282+
expect(inner_els[1].classList.contains("two")).toBe(false);
283+
expect(inner_els[1].classList.contains("three")).toBe(true);
284+
});
285+
it("Remove wildcard infix class", function () {
286+
document.body.innerHTML = `
287+
<div class="one icon-1-alert"></div>
288+
<div class="two icon-2-alert icon-3-alert"></div>
289+
`;
290+
const inner_els = document.querySelectorAll("div");
291+
utils.removeWildcardClass(inner_els, "icon-*-alert");
292+
expect(inner_els[0].classList.contains("one")).toBe(true);
293+
expect(inner_els[0].classList.contains("icon-1-alert")).toBe(false);
294+
expect(inner_els[1].classList.contains("two")).toBe(true);
295+
expect(inner_els[1].classList.contains("icon-2-alert")).toBe(false);
296+
expect(inner_els[1].classList.contains("icon-3-alert")).toBe(false);
297+
});
251298
});
252299
});
253300

0 commit comments

Comments
 (0)