css

package module
v0.0.0-...-33d8fb0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 4, 2019 License: MIT Imports: 8 Imported by: 1

README

* css selectors for go [[https://travis-ci.org/niklasfasching/css.svg?branch=master]]
Yet another package implementing a css selector compiler for go - see e.g. [[https://github.com/ericchiang/css/][github.com/ericchiang/css]] and [[https://github.com/andybalholm/cascadia][github.com/andybalholm/cascadia]] for prior art.

#+begin_src sh
go get github.com/niklasfasching/css
#+end_src

Differentiating features:
- Extensible (allows for user defined pseudo-classes/functions, matchers and combinators)
- Conversion from compiled Selector back to Selector string
- Small (~800 LOC), simple/modular (separation into lexing, parsing & selecting) and fast (benchmarks put it head to head with cascadia)

#+begin_src go
import (
	"log"
	"strings"

	"github.com/niklasfasching/css"
	"golang.org/x/net/html"
)

func main() {
	doc, _ := html.Parse(strings.NewReader(`
      <p>
      <span class="a">apple</span>
      <span class="b">banana</span>
      <span class="b">berry</span>
      <span class="c">pear</span>
      </p>
    `))

	selector := css.MustCompile("p > span.b")
	nodes := css.All(selector, doc)
	for _, n := range nodes {
		var s strings.Builder
		html.Render(&s, n)
		log.Println(s.String())
	}
	// <span class="b">banana</span>
	// <span class="b">berry</span>

	log.Printf("Converted back to string: %s", selector) // Converted back to string: p > span.b

	// easy to add your own custom pseudo classes, pseudo functions, matchers & combinators
	css.PseudoClasses["my-pseudo-p"] = func(n *html.Node) bool { return n.Data == "p" },
	selector = css.MustCompile(":my-pseudo-p")
	var s strings.Builder
	html.Render(&s, css.First(selector, doc))
	log.Println(s.String()) // <p>...</p>
}
#+end_src

** but why?
for fun

- It seemed easy enough to do
- I've been really into writing parsers lately and this felt a belt more complicated than a lisp
- I enjoy web scraping and wanted to learn more about underlying concepts
- I wanted to learn more about profiling go - and this seemed like a good playground
- The existing css libraries cannot be extended / customized

* resources
- https://github.com/andybalholm/cascadia
- https://github.com/ericchiang/css
- https://github.com/fb55/css-select
- [[https://webcache.googleusercontent.com/search?q=cache:OaB_kAprZssJ:https://www.w3.org/TR/2018/CR-selectors-3-20180130/][https://www.w3.org/TR/2018/CR-selectors-3-20180130/]]

Documentation

Overview

https://drafts.csswg.org/cssom/#common-serializing-idioms

https://www.w3.org/TR/2018/CR-selectors-3-20180130/#w3cselgrammar

Index

Constants

This section is empty.

Variables

View Source
var Combinators = map[string]func(Selector, Selector) Selector{
	" ": func(s1, s2 Selector) Selector { return &DescendantSelector{s1, s2} },
	">": func(s1, s2 Selector) Selector { return &ChildSelector{s1, s2} },
	"+": func(s1, s2 Selector) Selector { return &NextSiblingSelector{s1, s2} },
	"~": func(s1, s2 Selector) Selector { return &SubsequentSiblingSelector{s1, s2} },
	",": func(s1, s2 Selector) Selector { return &UnionSelector{s1, s2} },
}
View Source
var Matchers = map[string]func(string, string) bool{
	"~=": includeMatch,
	"|=": func(av, sv string) bool { return av == sv || strings.HasPrefix(av, sv+"-") },
	"^=": func(av, sv string) bool { return sv != "" && strings.HasPrefix(av, sv) },
	"$=": func(av, sv string) bool { return sv != "" && strings.HasSuffix(av, sv) },
	"*=": func(av, sv string) bool { return strings.Contains(av, sv) },
	"=":  func(av, sv string) bool { return av == sv },
	"":   func(string, string) bool { return true },
}
View Source
var PseudoClasses = map[string]func(*html.Node) bool{
	"root":          isRoot,
	"empty":         isEmpty,
	"checked":       func(n *html.Node) bool { return isInput(n) && hasAttribute(n, "checked") },
	"disabled":      func(n *html.Node) bool { return isInput(n) && hasAttribute(n, "disabled") },
	"enabled":       func(n *html.Node) bool { return isInput(n) && !hasAttribute(n, "disabled") },
	"optional":      func(n *html.Node) bool { return isInput(n) && !hasAttribute(n, "required") },
	"required":      func(n *html.Node) bool { return isInput(n) && hasAttribute(n, "required") },
	"read-only":     func(n *html.Node) bool { return isInput(n) && hasAttribute(n, "readonly") },
	"read-write":    func(n *html.Node) bool { return isInput(n) && !hasAttribute(n, "readonly") },
	"first-child":   nthSiblingCompiled(func(n *html.Node) *html.Node { return n.PrevSibling }, "1", false),
	"first-of-type": nthSiblingCompiled(func(n *html.Node) *html.Node { return n.PrevSibling }, "1", true),
	"last-child":    nthSiblingCompiled(func(n *html.Node) *html.Node { return n.NextSibling }, "1", false),
	"last-of-type":  nthSiblingCompiled(func(n *html.Node) *html.Node { return n.NextSibling }, "1", true),
	"only-child":    onlyChild(false),
	"only-of-type":  onlyChild(true),
}
View Source
var PseudoFunctions = map[string]func(string) (func(*html.Node) bool, error){
	"not":              nil,
	"nth-child":        nthSibling(func(n *html.Node) *html.Node { return n.PrevSibling }, false),
	"nth-last-child":   nthSibling(func(n *html.Node) *html.Node { return n.NextSibling }, false),
	"nth-of-type":      nthSibling(func(n *html.Node) *html.Node { return n.PrevSibling }, true),
	"nth-last-of-type": nthSibling(func(n *html.Node) *html.Node { return n.NextSibling }, true),
	"contains":         contains,
}

Functions

func All

func All(s Selector, n *html.Node) []*html.Node

func EscapeIdentifier

func EscapeIdentifier(unescaped string) (escaped string)

func EscapeString

func EscapeString(unescaped string) (escaped string)

func First

func First(s Selector, n *html.Node) *html.Node

func Unescape

func Unescape(escaped string) (unescaped string)

Types

type AttributeSelector

type AttributeSelector struct {
	Key   string
	Value string
	Type  string
	// contains filtered or unexported fields
}

func (*AttributeSelector) Match

func (s *AttributeSelector) Match(n *html.Node) bool

func (*AttributeSelector) String

func (s *AttributeSelector) String() string

type ChildSelector

type ChildSelector struct {
	Parent   Selector
	Selector Selector
}

func (*ChildSelector) Match

func (s *ChildSelector) Match(n *html.Node) bool

func (*ChildSelector) String

func (s *ChildSelector) String() string

type ClassSelector

type ClassSelector struct{ *AttributeSelector }

func (*ClassSelector) String

func (s *ClassSelector) String() string

type DescendantSelector

type DescendantSelector struct {
	Ancestor Selector
	Selector Selector
}

func (*DescendantSelector) Match

func (s *DescendantSelector) Match(n *html.Node) bool

func (*DescendantSelector) String

func (s *DescendantSelector) String() string

type ElementSelector

type ElementSelector struct {
	Element string
}

func (*ElementSelector) Match

func (s *ElementSelector) Match(n *html.Node) bool

func (*ElementSelector) String

func (s *ElementSelector) String() string

type IDSelector

type IDSelector struct{ *AttributeSelector }

func (*IDSelector) String

func (s *IDSelector) String() string

type NextSiblingSelector

type NextSiblingSelector struct {
	Sibling  Selector
	Selector Selector
}

func (*NextSiblingSelector) Match

func (s *NextSiblingSelector) Match(n *html.Node) bool

func (*NextSiblingSelector) String

func (s *NextSiblingSelector) String() string

type PseudoFunctionSelector

type PseudoFunctionSelector struct {
	Name string
	Args string
	// contains filtered or unexported fields
}

func (*PseudoFunctionSelector) Match

func (s *PseudoFunctionSelector) Match(n *html.Node) bool

func (*PseudoFunctionSelector) String

func (s *PseudoFunctionSelector) String() string

type PseudoSelector

type PseudoSelector struct {
	Name string
	// contains filtered or unexported fields
}

func (*PseudoSelector) Match

func (s *PseudoSelector) Match(n *html.Node) bool

func (*PseudoSelector) String

func (s *PseudoSelector) String() string

type Selector

type Selector interface {
	Match(*html.Node) bool
	String() string
}

func Compile

func Compile(selector string) (Selector, error)

func MustCompile

func MustCompile(selector string) Selector

type SelectorSequence

type SelectorSequence struct {
	Selectors []Selector
}

func (*SelectorSequence) Match

func (s *SelectorSequence) Match(n *html.Node) bool

func (*SelectorSequence) String

func (s *SelectorSequence) String() string

type SubsequentSiblingSelector

type SubsequentSiblingSelector struct {
	Sibling  Selector
	Selector Selector
}

func (*SubsequentSiblingSelector) Match

func (s *SubsequentSiblingSelector) Match(n *html.Node) bool

func (*SubsequentSiblingSelector) String

func (s *SubsequentSiblingSelector) String() string

type UnionSelector

type UnionSelector struct {
	SelectorA Selector
	SelectorB Selector
}

func (*UnionSelector) Match

func (s *UnionSelector) Match(n *html.Node) bool

func (*UnionSelector) String

func (s *UnionSelector) String() string

type UniversalSelector

type UniversalSelector struct {
	Element string
}

func (*UniversalSelector) Match

func (s *UniversalSelector) Match(n *html.Node) bool

func (*UniversalSelector) String

func (s *UniversalSelector) String() string

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL