diff --git a/property.go b/property.go index 5c8df86..bda736a 100644 --- a/property.go +++ b/property.go @@ -167,7 +167,7 @@ func (s *Selection) RemoveClass(class ...string) *Selection { } else { classes, attr := getClassesAndAttr(n) for _, rcl := range rclasses { - classes = removeClassFromClasses(classes, rcl) + classes = strings.ReplaceAll(classes, " "+rcl+" ", " ") } setClasses(n, attr, classes) @@ -191,8 +191,9 @@ func (s *Selection) ToggleClass(class ...string) *Selection { for _, n := range s.Nodes { classes, attr := getClassesAndAttr(n) for _, tcl := range tcls { - if strings.Contains(classes, " "+tcl+" ") { - classes = removeClassFromClasses(classes, tcl) + spaceAroundTcl := " " + tcl + " " + if strings.Contains(classes, spaceAroundTcl) { + classes = strings.ReplaceAll(classes, spaceAroundTcl, " ") } else { classes += tcl + " " } @@ -204,25 +205,6 @@ func (s *Selection) ToggleClass(class ...string) *Selection { return s } -// removeClassFromClasses removes every occurrence of cl from the normalized -// class string (a class list wrapped in spaces, as returned by -// getClassesAndAttr). -// -// A single strings.ReplaceAll pass is not enough: two adjacent occurrences -// share the space between them, so " a a " contains only one non-overlapping -// " a " and the second one survives the replacement. Replacing one at a time -// re-scans from the start and reaches it. -func removeClassFromClasses(classes, cl string) string { - target := " " + cl + " " - for { - replaced := strings.Replace(classes, target, " ", 1) - if replaced == classes { - return classes - } - classes = replaced - } -} - func getAttributePtr(attrName string, n *html.Node) *html.Attribute { if n == nil { return nil diff --git a/property_test.go b/property_test.go index e74040b..1095dcc 100644 --- a/property_test.go +++ b/property_test.go @@ -250,62 +250,3 @@ func TestToggleClass(t *testing.T) { t.Errorf("Expected #nf1 to have no classes, have %q", a) } } - -func TestRemoveClassRepeatedInSource(t *testing.T) { - cases := []struct { - class string - remove string - expected string - }{ - {"a a", "a", ""}, - {"a a a", "a", ""}, - {"a b a", "a", "b"}, - {"a b a b", "a", "b b"}, - {"a b a b", "a b", ""}, - {"a a b", "a", "b"}, - {"b a a", "a", "b"}, - } - - for _, c := range cases { - doc, err := NewDocumentFromReader(strings.NewReader(`
`)) - if err != nil { - t.Fatal(err) - } - - sel := doc.Find("#t") - sel.RemoveClass(c.remove) - - got, _ := sel.Attr("class") - if got != c.expected { - t.Errorf("class=%q RemoveClass(%q): got class %q, want %q", c.class, c.remove, got, c.expected) - } - - for _, removed := range strings.Fields(c.remove) { - if sel.HasClass(removed) { - t.Errorf("class=%q RemoveClass(%q): still has class %q", c.class, c.remove, removed) - } - } - } -} - -func TestToggleClassRepeatedInSource(t *testing.T) { - doc, err := NewDocumentFromReader(strings.NewReader(``)) - if err != nil { - t.Fatal(err) - } - - sel := doc.Find("#t") - - sel.ToggleClass("a") - if sel.HasClass("a") { - t.Error("expected #t to not have class a after toggling it off") - } - if !sel.HasClass("b") { - t.Error("expected #t to keep class b") - } - - sel.ToggleClass("a") - if !sel.HasClass("a") { - t.Error("expected #t to have class a after toggling it back on") - } -}