├── .gitattributes
├── .github
├── FUNDING.yml
├── dependabot.yml
└── workflows
│ └── test.yml
├── .gitignore
├── LICENSE
├── README.md
├── array.go
├── array_test.go
├── bench
├── v0.1.0
├── v0.1.1
├── v0.1.1-v0.2.1-go1.1rc1.svg
├── v0.2.0
├── v0.2.0-v0.2.1-go1.1rc1.svg
├── v0.2.1-go1.1rc1
├── v0.3.0
├── v0.3.2-go1.2
├── v0.3.2-go1.2-take2
├── v0.3.2-go1.2rc1
├── v1.0.0-go1.7
├── v1.0.1a-go1.7
├── v1.0.1b-go1.7
└── v1.0.1c-go1.7
├── bench_array_test.go
├── bench_example_test.go
├── bench_expand_test.go
├── bench_filter_test.go
├── bench_iteration_test.go
├── bench_property_test.go
├── bench_query_test.go
├── bench_traversal_test.go
├── doc.go
├── doc
└── tips.md
├── example_test.go
├── expand.go
├── expand_test.go
├── filter.go
├── filter_test.go
├── go.mod
├── go.sum
├── iteration.go
├── iteration_test.go
├── manipulation.go
├── manipulation_test.go
├── misc
└── git
│ └── pre-commit
├── property.go
├── property_test.go
├── query.go
├── query_test.go
├── testdata
├── gotesting.html
├── gowiki.html
├── metalreview.html
├── page.html
├── page2.html
└── page3.html
├── traversal.go
├── traversal_test.go
├── type.go
├── type_test.go
├── utilities.go
└── utilities_test.go
/.gitattributes:
--------------------------------------------------------------------------------
1 | testdata/* linguist-vendored
2 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | github: [mna]
2 | custom: ["https://www.buymeacoffee.com/mna"]
3 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | # Maintain dependencies for Go
4 | - package-ecosystem: "gomod"
5 | directory: "/"
6 | schedule:
7 | interval: "daily"
8 | # Enable version updates for GitHub action workflows
9 | - package-ecosystem: "github-actions"
10 | directory: "/"
11 | schedule:
12 | interval: "daily"
13 |
--------------------------------------------------------------------------------
/.github/workflows/test.yml:
--------------------------------------------------------------------------------
1 | name: test
2 | on: [push, pull_request]
3 |
4 | env:
5 | GOPROXY: https://proxy.golang.org,direct
6 |
7 | jobs:
8 | test:
9 | strategy:
10 | matrix:
11 | go-version: [1.23.x, 1.24.x]
12 | os: [ubuntu-latest, macos-latest, windows-latest]
13 | runs-on: ${{ matrix.os }}
14 |
15 | steps:
16 | - name: Checkout code
17 | uses: actions/checkout@v4
18 |
19 | - name: Install Go
20 | uses: actions/setup-go@v5
21 | with:
22 | go-version: ${{ matrix.go-version }}
23 |
24 | - name: Test
25 | run: go test ./... -v -cover
26 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # editor temporary files
2 | *.sublime-*
3 | .DS_Store
4 | *.swp
5 | #*.*#
6 | tags
7 |
8 | # direnv config
9 | .env*
10 |
11 | # test binaries
12 | *.test
13 |
14 | # coverage and profilte outputs
15 | *.out
16 |
17 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2012-2021, Martin Angers & Contributors
2 | All rights reserved.
3 |
4 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
5 |
6 | * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
7 |
8 | * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
9 |
10 | * Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
11 |
12 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
13 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # goquery - a little like that j-thing, only in Go
2 |
3 | [](https://github.com/PuerkitoBio/goquery/actions)
4 | [](https://pkg.go.dev/github.com/PuerkitoBio/goquery)
5 | [](https://sourcegraph.com/github.com/PuerkitoBio/goquery?badge)
6 |
7 | goquery brings a syntax and a set of features similar to [jQuery][] to the [Go language][go]. It is based on Go's [net/html package][html] and the CSS Selector library [cascadia][]. Since the net/html parser returns nodes, and not a full-featured DOM tree, jQuery's stateful manipulation functions (like height(), css(), detach()) have been left off.
8 |
9 | Also, because the net/html parser requires UTF-8 encoding, so does goquery: it is the caller's responsibility to ensure that the source document provides UTF-8 encoded HTML. See the [wiki][] for various options to do this.
10 |
11 | Syntax-wise, it is as close as possible to jQuery, with the same function names when possible, and that warm and fuzzy chainable interface. jQuery being the ultra-popular library that it is, I felt that writing a similar HTML-manipulating library was better to follow its API than to start anew (in the same spirit as Go's `fmt` package), even though some of its methods are less than intuitive (looking at you, [index()][index]...).
12 |
13 | ## Table of Contents
14 |
15 | * [Installation](#installation)
16 | * [Changelog](#changelog)
17 | * [API](#api)
18 | * [Examples](#examples)
19 | * [Related Projects](#related-projects)
20 | * [Support](#support)
21 | * [License](#license)
22 |
23 | ## Installation
24 |
25 | Required Go version:
26 |
27 | * Starting with version `v1.10.0` of goquery, Go 1.23+ is required due to the use of function-based iterators.
28 | * For `v1.9.0` of goquery, Go 1.18+ is required due to the use of generics.
29 | * For previous goquery versions, a Go version of 1.1+ was required because of the `net/html` dependency.
30 |
31 | Ongoing goquery development is tested on the latest 2 versions of Go.
32 |
33 | $ go get github.com/PuerkitoBio/goquery
34 |
35 | (optional) To run unit tests:
36 |
37 | $ cd $GOPATH/src/github.com/PuerkitoBio/goquery
38 | $ go test
39 |
40 | (optional) To run benchmarks (warning: it runs for a few minutes):
41 |
42 | $ cd $GOPATH/src/github.com/PuerkitoBio/goquery
43 | $ go test -bench=".*"
44 |
45 | ## Changelog
46 |
47 | **Note that goquery's API is now stable, and will not break.**
48 |
49 | * **2025-04-11 (v1.10.3)** : Update `go.mod` dependencies, small optimization (thanks [@myxzlpltk](https://github.com/myxzlpltk)).
50 | * **2025-02-13 (v1.10.2)** : Update `go.mod` dependencies, add go1.24 to the test matrix.
51 | * **2024-12-26 (v1.10.1)** : Update `go.mod` dependencies.
52 | * **2024-09-06 (v1.10.0)** : Add `EachIter` which provides an iterator that can be used in `for..range` loops on the `*Selection` object. **goquery now requires Go version 1.23+** (thanks [@amikai](https://github.com/amikai)).
53 | * **2024-09-06 (v1.9.3)** : Update `go.mod` dependencies.
54 | * **2024-04-29 (v1.9.2)** : Update `go.mod` dependencies.
55 | * **2024-02-29 (v1.9.1)** : Improve allocation and performance of the `Map` function and `Selection.Map` method, better document the cascadia differences (thanks [@jwilsson](https://github.com/jwilsson)).
56 | * **2024-02-22 (v1.9.0)** : Add a generic `Map` function, **goquery now requires Go version 1.18+** (thanks [@Fesaa](https://github.com/Fesaa)).
57 | * **2023-02-18 (v1.8.1)** : Update `go.mod` dependencies, update CI workflow.
58 | * **2021-10-25 (v1.8.0)** : Add `Render` function to render a `Selection` to an `io.Writer` (thanks [@anthonygedeon](https://github.com/anthonygedeon)).
59 | * **2021-07-11 (v1.7.1)** : Update go.mod dependencies and add dependabot config (thanks [@jauderho](https://github.com/jauderho)).
60 | * **2021-06-14 (v1.7.0)** : Add `Single` and `SingleMatcher` functions to optimize first-match selection (thanks [@gdollardollar](https://github.com/gdollardollar)).
61 | * **2021-01-11 (v1.6.1)** : Fix panic when calling `{Prepend,Append,Set}Html` on a `Selection` that contains non-Element nodes.
62 | * **2020-10-08 (v1.6.0)** : Parse html in context of the container node for all functions that deal with html strings (`AfterHtml`, `AppendHtml`, etc.). Thanks to [@thiemok][thiemok] and [@davidjwilkins][djw] for their work on this.
63 | * **2020-02-04 (v1.5.1)** : Update module dependencies.
64 | * **2018-11-15 (v1.5.0)** : Go module support (thanks @Zaba505).
65 | * **2018-06-07 (v1.4.1)** : Add `NewDocumentFromReader` examples.
66 | * **2018-03-24 (v1.4.0)** : Deprecate `NewDocument(url)` and `NewDocumentFromResponse(response)`.
67 | * **2018-01-28 (v1.3.0)** : Add `ToEnd` constant to `Slice` until the end of the selection (thanks to @davidjwilkins for raising the issue).
68 | * **2018-01-11 (v1.2.0)** : Add `AddBack*` and deprecate `AndSelf` (thanks to @davidjwilkins).
69 | * **2017-02-12 (v1.1.0)** : Add `SetHtml` and `SetText` (thanks to @glebtv).
70 | * **2016-12-29 (v1.0.2)** : Optimize allocations for `Selection.Text` (thanks to @radovskyb).
71 | * **2016-08-28 (v1.0.1)** : Optimize performance for large documents.
72 | * **2016-07-27 (v1.0.0)** : Tag version 1.0.0.
73 | * **2016-06-15** : Invalid selector strings internally compile to a `Matcher` implementation that never matches any node (instead of a panic). So for example, `doc.Find("~")` returns an empty `*Selection` object.
74 | * **2016-02-02** : Add `NodeName` utility function similar to the DOM's `nodeName` property. It returns the tag name of the first element in a selection, and other relevant values of non-element nodes (see [doc][] for details). Add `OuterHtml` utility function similar to the DOM's `outerHTML` property (named `OuterHtml` in small caps for consistency with the existing `Html` method on the `Selection`).
75 | * **2015-04-20** : Add `AttrOr` helper method to return the attribute's value or a default value if absent. Thanks to [piotrkowalczuk][piotr].
76 | * **2015-02-04** : Add more manipulation functions - Prepend* - thanks again to [Andrew Stone][thatguystone].
77 | * **2014-11-28** : Add more manipulation functions - ReplaceWith*, Wrap* and Unwrap - thanks again to [Andrew Stone][thatguystone].
78 | * **2014-11-07** : Add manipulation functions (thanks to [Andrew Stone][thatguystone]) and `*Matcher` functions, that receive compiled cascadia selectors instead of selector strings, thus avoiding potential panics thrown by goquery via `cascadia.MustCompile` calls. This results in better performance (selectors can be compiled once and reused) and more idiomatic error handling (you can handle cascadia's compilation errors, instead of recovering from panics, which had been bugging me for a long time). Note that the actual type expected is a `Matcher` interface, that `cascadia.Selector` implements. Other matcher implementations could be used.
79 | * **2014-11-06** : Change import paths of net/html to golang.org/x/net/html (see https://groups.google.com/forum/#!topic/golang-nuts/eD8dh3T9yyA). Make sure to update your code to use the new import path too when you call goquery with `html.Node`s.
80 | * **v0.3.2** : Add `NewDocumentFromReader()` (thanks jweir) which allows creating a goquery document from an io.Reader.
81 | * **v0.3.1** : Add `NewDocumentFromResponse()` (thanks assassingj) which allows creating a goquery document from an http response.
82 | * **v0.3.0** : Add `EachWithBreak()` which allows to break out of an `Each()` loop by returning false. This function was added instead of changing the existing `Each()` to avoid breaking compatibility.
83 | * **v0.2.1** : Make go-getable, now that [go.net/html is Go1.0-compatible][gonet] (thanks to @matrixik for pointing this out).
84 | * **v0.2.0** : Add support for negative indices in Slice(). **BREAKING CHANGE** `Document.Root` is removed, `Document` is now a `Selection` itself (a selection of one, the root element, just like `Document.Root` was before). Add jQuery's Closest() method.
85 | * **v0.1.1** : Add benchmarks to use as baseline for refactorings, refactor Next...() and Prev...() methods to use the new html package's linked list features (Next/PrevSibling, FirstChild). Good performance boost (40+% in some cases).
86 | * **v0.1.0** : Initial release.
87 |
88 | ## API
89 |
90 | goquery exposes two structs, `Document` and `Selection`, and the `Matcher` interface. Unlike jQuery, which is loaded as part of a DOM document, and thus acts on its containing document, goquery doesn't know which HTML document to act upon. So it needs to be told, and that's what the `Document` type is for. It holds the root document node as the initial Selection value to manipulate.
91 |
92 | jQuery often has many variants for the same function (no argument, a selector string argument, a jQuery object argument, a DOM element argument, ...). Instead of exposing the same features in goquery as a single method with variadic empty interface arguments, statically-typed signatures are used following this naming convention:
93 |
94 | * When the jQuery equivalent can be called with no argument, it has the same name as jQuery for the no argument signature (e.g.: `Prev()`), and the version with a selector string argument is called `XxxFiltered()` (e.g.: `PrevFiltered()`)
95 | * When the jQuery equivalent **requires** one argument, the same name as jQuery is used for the selector string version (e.g.: `Is()`)
96 | * The signatures accepting a jQuery object as argument are defined in goquery as `XxxSelection()` and take a `*Selection` object as argument (e.g.: `FilterSelection()`)
97 | * The signatures accepting a DOM element as argument in jQuery are defined in goquery as `XxxNodes()` and take a variadic argument of type `*html.Node` (e.g.: `FilterNodes()`)
98 | * The signatures accepting a function as argument in jQuery are defined in goquery as `XxxFunction()` and take a function as argument (e.g.: `FilterFunction()`)
99 | * The goquery methods that can be called with a selector string have a corresponding version that take a `Matcher` interface and are defined as `XxxMatcher()` (e.g.: `IsMatcher()`)
100 |
101 | Utility functions that are not in jQuery but are useful in Go are implemented as functions (that take a `*Selection` as parameter), to avoid a potential naming clash on the `*Selection`'s methods (reserved for jQuery-equivalent behaviour).
102 |
103 | The complete [package reference documentation can be found here][doc].
104 |
105 | Please note that Cascadia's selectors do not necessarily match all supported selectors of jQuery (Sizzle). See the [cascadia project][cascadia] for details. Also, the selectors work more like the DOM's `querySelectorAll`, than jQuery's matchers - they have no concept of contextual matching (for some concrete examples of what that means, see [this ticket](https://github.com/andybalholm/cascadia/issues/61)). In practice, it doesn't matter very often but it's something worth mentioning. Invalid selector strings compile to a `Matcher` that fails to match any node. Behaviour of the various functions that take a selector string as argument follows from that fact, e.g. (where `~` is an invalid selector string):
106 |
107 | * `Find("~")` returns an empty selection because the selector string doesn't match anything.
108 | * `Add("~")` returns a new selection that holds the same nodes as the original selection, because it didn't add any node (selector string didn't match anything).
109 | * `ParentsFiltered("~")` returns an empty selection because the selector string doesn't match anything.
110 | * `ParentsUntil("~")` returns all parents of the selection because the selector string didn't match any element to stop before the top element.
111 |
112 | ## Examples
113 |
114 | See some tips and tricks in the [wiki][].
115 |
116 | Adapted from example_test.go:
117 |
118 | ```Go
119 | package main
120 |
121 | import (
122 | "fmt"
123 | "log"
124 | "net/http"
125 |
126 | "github.com/PuerkitoBio/goquery"
127 | )
128 |
129 | func ExampleScrape() {
130 | // Request the HTML page.
131 | res, err := http.Get("http://metalsucks.net")
132 | if err != nil {
133 | log.Fatal(err)
134 | }
135 | defer res.Body.Close()
136 | if res.StatusCode != 200 {
137 | log.Fatalf("status code error: %d %s", res.StatusCode, res.Status)
138 | }
139 |
140 | // Load the HTML document
141 | doc, err := goquery.NewDocumentFromReader(res.Body)
142 | if err != nil {
143 | log.Fatal(err)
144 | }
145 |
146 | // Find the review items
147 | doc.Find(".left-content article .post-title").Each(func(i int, s *goquery.Selection) {
148 | // For each item found, get the title
149 | title := s.Find("a").Text()
150 | fmt.Printf("Review %d: %s\n", i, title)
151 | })
152 | }
153 |
154 | func main() {
155 | ExampleScrape()
156 | }
157 | ```
158 |
159 | ## Related Projects
160 |
161 | - [Goq][goq], an HTML deserialization and scraping library based on goquery and struct tags.
162 | - [andybalholm/cascadia][cascadia], the CSS selector library used by goquery.
163 | - [suntong/cascadia][cascadiacli], a command-line interface to the cascadia CSS selector library, useful to test selectors.
164 | - [gocolly/colly](https://github.com/gocolly/colly), a lightning fast and elegant Scraping Framework
165 | - [gnulnx/goperf](https://github.com/gnulnx/goperf), a website performance test tool that also fetches static assets.
166 | - [MontFerret/ferret](https://github.com/MontFerret/ferret), declarative web scraping.
167 | - [tacusci/berrycms](https://github.com/tacusci/berrycms), a modern simple to use CMS with easy to write plugins
168 | - [Dataflow kit](https://github.com/slotix/dataflowkit), Web Scraping framework for Gophers.
169 | - [Geziyor](https://github.com/geziyor/geziyor), a fast web crawling & scraping framework for Go. Supports JS rendering.
170 | - [Pagser](https://github.com/foolin/pagser), a simple, easy, extensible, configurable HTML parser to struct based on goquery and struct tags.
171 | - [stitcherd](https://github.com/vhodges/stitcherd), A server for doing server side includes using css selectors and DOM updates.
172 | - [goskyr](https://github.com/jakopako/goskyr), an easily configurable command-line scraper written in Go.
173 | - [goGetJS](https://github.com/davemolk/goGetJS), a tool for extracting, searching, and saving JavaScript files (with optional headless browser).
174 | - [fitter](https://github.com/PxyUp/fitter), a tool for selecting values from JSON, XML, HTML and XPath formatted pages.
175 | - [seltabl](github.com/conneroisu/seltabl), an orm-like package and supporting language server for extracting values from HTML
176 |
177 | ## Support
178 |
179 | There are a number of ways you can support the project:
180 |
181 | * Use it, star it, build something with it, spread the word!
182 | - If you do build something open-source or otherwise publicly-visible, let me know so I can add it to the [Related Projects](#related-projects) section!
183 | * Raise issues to improve the project (note: doc typos and clarifications are issues too!)
184 | - Please search existing issues before opening a new one - it may have already been addressed.
185 | * Pull requests: please discuss new code in an issue first, unless the fix is really trivial.
186 | - Make sure new code is tested.
187 | - Be mindful of existing code - PRs that break existing code have a high probability of being declined, unless it fixes a serious issue.
188 | * Sponsor the developer
189 | - See the Github Sponsor button at the top of the repo on github
190 | - or via BuyMeACoffee.com, below
191 |
192 |
193 |
194 | ## License
195 |
196 | The [BSD 3-Clause license][bsd], the same as the [Go language][golic]. Cascadia's license is [here][caslic].
197 |
198 | [jquery]: https://jquery.com/
199 | [go]: https://go.dev/
200 | [cascadia]: https://github.com/andybalholm/cascadia
201 | [cascadiacli]: https://github.com/suntong/cascadia
202 | [bsd]: https://opensource.org/licenses/BSD-3-Clause
203 | [golic]: https://go.dev/LICENSE
204 | [caslic]: https://github.com/andybalholm/cascadia/blob/master/LICENSE
205 | [doc]: https://pkg.go.dev/github.com/PuerkitoBio/goquery
206 | [index]: https://api.jquery.com/index/
207 | [gonet]: https://github.com/golang/net/
208 | [html]: https://pkg.go.dev/golang.org/x/net/html
209 | [wiki]: https://github.com/PuerkitoBio/goquery/wiki/Tips-and-tricks
210 | [thatguystone]: https://github.com/thatguystone
211 | [piotr]: https://github.com/piotrkowalczuk
212 | [goq]: https://github.com/andrewstuart/goq
213 | [thiemok]: https://github.com/thiemok
214 | [djw]: https://github.com/davidjwilkins
215 |
--------------------------------------------------------------------------------
/array.go:
--------------------------------------------------------------------------------
1 | package goquery
2 |
3 | import (
4 | "golang.org/x/net/html"
5 | )
6 |
7 | const (
8 | maxUint = ^uint(0)
9 | maxInt = int(maxUint >> 1)
10 |
11 | // ToEnd is a special index value that can be used as end index in a call
12 | // to Slice so that all elements are selected until the end of the Selection.
13 | // It is equivalent to passing (*Selection).Length().
14 | ToEnd = maxInt
15 | )
16 |
17 | // First reduces the set of matched elements to the first in the set.
18 | // It returns a new Selection object, and an empty Selection object if the
19 | // the selection is empty.
20 | func (s *Selection) First() *Selection {
21 | return s.Eq(0)
22 | }
23 |
24 | // Last reduces the set of matched elements to the last in the set.
25 | // It returns a new Selection object, and an empty Selection object if
26 | // the selection is empty.
27 | func (s *Selection) Last() *Selection {
28 | return s.Eq(-1)
29 | }
30 |
31 | // Eq reduces the set of matched elements to the one at the specified index.
32 | // If a negative index is given, it counts backwards starting at the end of the
33 | // set. It returns a new Selection object, and an empty Selection object if the
34 | // index is invalid.
35 | func (s *Selection) Eq(index int) *Selection {
36 | if index < 0 {
37 | index += len(s.Nodes)
38 | }
39 |
40 | if index >= len(s.Nodes) || index < 0 {
41 | return newEmptySelection(s.document)
42 | }
43 |
44 | return s.Slice(index, index+1)
45 | }
46 |
47 | // Slice reduces the set of matched elements to a subset specified by a range
48 | // of indices. The start index is 0-based and indicates the index of the first
49 | // element to select. The end index is 0-based and indicates the index at which
50 | // the elements stop being selected (the end index is not selected).
51 | //
52 | // The indices may be negative, in which case they represent an offset from the
53 | // end of the selection.
54 | //
55 | // The special value ToEnd may be specified as end index, in which case all elements
56 | // until the end are selected. This works both for a positive and negative start
57 | // index.
58 | func (s *Selection) Slice(start, end int) *Selection {
59 | if start < 0 {
60 | start += len(s.Nodes)
61 | }
62 | if end == ToEnd {
63 | end = len(s.Nodes)
64 | } else if end < 0 {
65 | end += len(s.Nodes)
66 | }
67 | return pushStack(s, s.Nodes[start:end])
68 | }
69 |
70 | // Get retrieves the underlying node at the specified index.
71 | // Get without parameter is not implemented, since the node array is available
72 | // on the Selection object.
73 | func (s *Selection) Get(index int) *html.Node {
74 | if index < 0 {
75 | index += len(s.Nodes) // Negative index gets from the end
76 | }
77 | return s.Nodes[index]
78 | }
79 |
80 | // Index returns the position of the first element within the Selection object
81 | // relative to its sibling elements.
82 | func (s *Selection) Index() int {
83 | if len(s.Nodes) > 0 {
84 | return newSingleSelection(s.Nodes[0], s.document).PrevAll().Length()
85 | }
86 | return -1
87 | }
88 |
89 | // IndexSelector returns the position of the first element within the
90 | // Selection object relative to the elements matched by the selector, or -1 if
91 | // not found.
92 | func (s *Selection) IndexSelector(selector string) int {
93 | if len(s.Nodes) > 0 {
94 | sel := s.document.Find(selector)
95 | return indexInSlice(sel.Nodes, s.Nodes[0])
96 | }
97 | return -1
98 | }
99 |
100 | // IndexMatcher returns the position of the first element within the
101 | // Selection object relative to the elements matched by the matcher, or -1 if
102 | // not found.
103 | func (s *Selection) IndexMatcher(m Matcher) int {
104 | if len(s.Nodes) > 0 {
105 | sel := s.document.FindMatcher(m)
106 | return indexInSlice(sel.Nodes, s.Nodes[0])
107 | }
108 | return -1
109 | }
110 |
111 | // IndexOfNode returns the position of the specified node within the Selection
112 | // object, or -1 if not found.
113 | func (s *Selection) IndexOfNode(node *html.Node) int {
114 | return indexInSlice(s.Nodes, node)
115 | }
116 |
117 | // IndexOfSelection returns the position of the first node in the specified
118 | // Selection object within this Selection object, or -1 if not found.
119 | func (s *Selection) IndexOfSelection(sel *Selection) int {
120 | if sel != nil && len(sel.Nodes) > 0 {
121 | return indexInSlice(s.Nodes, sel.Nodes[0])
122 | }
123 | return -1
124 | }
125 |
--------------------------------------------------------------------------------
/array_test.go:
--------------------------------------------------------------------------------
1 | package goquery
2 |
3 | import (
4 | "testing"
5 | )
6 |
7 | func TestFirst(t *testing.T) {
8 | sel := Doc().Find(".pvk-content").First()
9 | assertLength(t, sel.Nodes, 1)
10 | }
11 |
12 | func TestFirstEmpty(t *testing.T) {
13 | sel := Doc().Find(".pvk-zzcontentzz").First()
14 | assertLength(t, sel.Nodes, 0)
15 | }
16 |
17 | func TestFirstInvalid(t *testing.T) {
18 | sel := Doc().Find("").First()
19 | assertLength(t, sel.Nodes, 0)
20 | }
21 |
22 | func TestFirstRollback(t *testing.T) {
23 | sel := Doc().Find(".pvk-content")
24 | sel2 := sel.First().End()
25 | assertEqual(t, sel, sel2)
26 | }
27 |
28 | func TestLast(t *testing.T) {
29 | sel := Doc().Find(".pvk-content").Last()
30 | assertLength(t, sel.Nodes, 1)
31 |
32 | // Should contain Footer
33 | foot := Doc().Find(".footer")
34 | if !sel.Contains(foot.Nodes[0]) {
35 | t.Error("Last .pvk-content should contain .footer.")
36 | }
37 | }
38 |
39 | func TestLastEmpty(t *testing.T) {
40 | sel := Doc().Find(".pvk-zzcontentzz").Last()
41 | assertLength(t, sel.Nodes, 0)
42 | }
43 |
44 | func TestLastInvalid(t *testing.T) {
45 | sel := Doc().Find("").Last()
46 | assertLength(t, sel.Nodes, 0)
47 | }
48 |
49 | func TestLastRollback(t *testing.T) {
50 | sel := Doc().Find(".pvk-content")
51 | sel2 := sel.Last().End()
52 | assertEqual(t, sel, sel2)
53 | }
54 |
55 | func TestEq(t *testing.T) {
56 | sel := Doc().Find(".pvk-content").Eq(1)
57 | assertLength(t, sel.Nodes, 1)
58 | }
59 |
60 | func TestEqNegative(t *testing.T) {
61 | sel := Doc().Find(".pvk-content").Eq(-1)
62 | assertLength(t, sel.Nodes, 1)
63 |
64 | // Should contain Footer
65 | foot := Doc().Find(".footer")
66 | if !sel.Contains(foot.Nodes[0]) {
67 | t.Error("Index -1 of .pvk-content should contain .footer.")
68 | }
69 | }
70 |
71 | func TestEqEmpty(t *testing.T) {
72 | sel := Doc().Find("something_random_that_does_not_exists").Eq(0)
73 | assertLength(t, sel.Nodes, 0)
74 | }
75 |
76 | func TestEqInvalid(t *testing.T) {
77 | sel := Doc().Find("").Eq(0)
78 | assertLength(t, sel.Nodes, 0)
79 | }
80 |
81 | func TestEqInvalidPositive(t *testing.T) {
82 | sel := Doc().Find(".pvk-content").Eq(3)
83 | assertLength(t, sel.Nodes, 0)
84 | }
85 |
86 | func TestEqInvalidNegative(t *testing.T) {
87 | sel := Doc().Find(".pvk-content").Eq(-4)
88 | assertLength(t, sel.Nodes, 0)
89 | }
90 |
91 | func TestEqRollback(t *testing.T) {
92 | sel := Doc().Find(".pvk-content")
93 | sel2 := sel.Eq(1).End()
94 | assertEqual(t, sel, sel2)
95 | }
96 |
97 | func TestSlice(t *testing.T) {
98 | sel := Doc().Find(".pvk-content").Slice(0, 2)
99 |
100 | assertLength(t, sel.Nodes, 2)
101 | assertSelectionIs(t, sel, "#pc1", "#pc2")
102 | }
103 |
104 | func TestSliceToEnd(t *testing.T) {
105 | sel := Doc().Find(".pvk-content").Slice(1, ToEnd)
106 |
107 | assertLength(t, sel.Nodes, 2)
108 | assertSelectionIs(t, sel.Eq(0), "#pc2")
109 | if _, ok := sel.Eq(1).Attr("id"); ok {
110 | t.Error("Want no attribute ID, got one")
111 | }
112 | }
113 |
114 | func TestSliceEmpty(t *testing.T) {
115 | defer assertPanic(t)
116 | Doc().Find("x").Slice(0, 2)
117 | }
118 |
119 | func TestSliceInvalid(t *testing.T) {
120 | defer assertPanic(t)
121 | Doc().Find("").Slice(0, 2)
122 | }
123 |
124 | func TestSliceInvalidToEnd(t *testing.T) {
125 | defer assertPanic(t)
126 | Doc().Find("").Slice(2, ToEnd)
127 | }
128 |
129 | func TestSliceOutOfBounds(t *testing.T) {
130 | defer assertPanic(t)
131 | Doc().Find(".pvk-content").Slice(2, 12)
132 | }
133 |
134 | func TestNegativeSliceStart(t *testing.T) {
135 | sel := Doc().Find(".container-fluid").Slice(-2, 3)
136 | assertLength(t, sel.Nodes, 1)
137 | assertSelectionIs(t, sel.Eq(0), "#cf3")
138 | }
139 |
140 | func TestNegativeSliceEnd(t *testing.T) {
141 | sel := Doc().Find(".container-fluid").Slice(1, -1)
142 | assertLength(t, sel.Nodes, 2)
143 | assertSelectionIs(t, sel.Eq(0), "#cf2")
144 | assertSelectionIs(t, sel.Eq(1), "#cf3")
145 | }
146 |
147 | func TestNegativeSliceBoth(t *testing.T) {
148 | sel := Doc().Find(".container-fluid").Slice(-3, -1)
149 | assertLength(t, sel.Nodes, 2)
150 | assertSelectionIs(t, sel.Eq(0), "#cf2")
151 | assertSelectionIs(t, sel.Eq(1), "#cf3")
152 | }
153 |
154 | func TestNegativeSliceToEnd(t *testing.T) {
155 | sel := Doc().Find(".container-fluid").Slice(-3, ToEnd)
156 | assertLength(t, sel.Nodes, 3)
157 | assertSelectionIs(t, sel, "#cf2", "#cf3", "#cf4")
158 | }
159 |
160 | func TestNegativeSliceOutOfBounds(t *testing.T) {
161 | defer assertPanic(t)
162 | Doc().Find(".container-fluid").Slice(-12, -7)
163 | }
164 |
165 | func TestSliceRollback(t *testing.T) {
166 | sel := Doc().Find(".pvk-content")
167 | sel2 := sel.Slice(0, 2).End()
168 | assertEqual(t, sel, sel2)
169 | }
170 |
171 | func TestGet(t *testing.T) {
172 | sel := Doc().Find(".pvk-content")
173 | node := sel.Get(1)
174 | if sel.Nodes[1] != node {
175 | t.Errorf("Expected node %v to be %v.", node, sel.Nodes[1])
176 | }
177 | }
178 |
179 | func TestGetNegative(t *testing.T) {
180 | sel := Doc().Find(".pvk-content")
181 | node := sel.Get(-3)
182 | if sel.Nodes[0] != node {
183 | t.Errorf("Expected node %v to be %v.", node, sel.Nodes[0])
184 | }
185 | }
186 |
187 | func TestGetInvalid(t *testing.T) {
188 | defer assertPanic(t)
189 | sel := Doc().Find(".pvk-content")
190 | sel.Get(129)
191 | }
192 |
193 | func TestIndex(t *testing.T) {
194 | sel := Doc().Find(".pvk-content")
195 | if i := sel.Index(); i != 1 {
196 | t.Errorf("Expected index of 1, got %v.", i)
197 | }
198 | }
199 |
200 | func TestIndexSelector(t *testing.T) {
201 | sel := Doc().Find(".hero-unit")
202 | if i := sel.IndexSelector("div"); i != 4 {
203 | t.Errorf("Expected index of 4, got %v.", i)
204 | }
205 | }
206 |
207 | func TestIndexSelectorInvalid(t *testing.T) {
208 | sel := Doc().Find(".hero-unit")
209 | if i := sel.IndexSelector(""); i != -1 {
210 | t.Errorf("Expected index of -1, got %v.", i)
211 | }
212 | }
213 |
214 | func TestIndexOfNode(t *testing.T) {
215 | sel := Doc().Find("div.pvk-gutter")
216 | if i := sel.IndexOfNode(sel.Nodes[1]); i != 1 {
217 | t.Errorf("Expected index of 1, got %v.", i)
218 | }
219 | }
220 |
221 | func TestIndexOfNilNode(t *testing.T) {
222 | sel := Doc().Find("div.pvk-gutter")
223 | if i := sel.IndexOfNode(nil); i != -1 {
224 | t.Errorf("Expected index of -1, got %v.", i)
225 | }
226 | }
227 |
228 | func TestIndexOfSelection(t *testing.T) {
229 | sel := Doc().Find("div")
230 | sel2 := Doc().Find(".hero-unit")
231 | if i := sel.IndexOfSelection(sel2); i != 4 {
232 | t.Errorf("Expected index of 4, got %v.", i)
233 | }
234 | }
235 |
--------------------------------------------------------------------------------
/bench/v0.1.0:
--------------------------------------------------------------------------------
1 | PASS
2 | BenchmarkFirst 20000000 92.9 ns/op
3 | BenchmarkLast 20000000 91.6 ns/op
4 | BenchmarkEq 20000000 90.6 ns/op
5 | BenchmarkSlice 20000000 86.7 ns/op
6 | BenchmarkGet 1000000000 2.14 ns/op
7 | BenchmarkIndex 500000 5308 ns/op
8 | --- BENCH: BenchmarkIndex
9 | bench_array_test.go:73: Index=3
10 | bench_array_test.go:73: Index=3
11 | bench_array_test.go:73: Index=3
12 | bench_array_test.go:73: Index=3
13 | BenchmarkIndexSelector 50000 54962 ns/op
14 | --- BENCH: BenchmarkIndexSelector
15 | bench_array_test.go:85: IndexSelector=4
16 | bench_array_test.go:85: IndexSelector=4
17 | bench_array_test.go:85: IndexSelector=4
18 | bench_array_test.go:85: IndexSelector=4
19 | BenchmarkIndexOfNode 100000000 11.4 ns/op
20 | --- BENCH: BenchmarkIndexOfNode
21 | bench_array_test.go:99: IndexOfNode=2
22 | bench_array_test.go:99: IndexOfNode=2
23 | bench_array_test.go:99: IndexOfNode=2
24 | bench_array_test.go:99: IndexOfNode=2
25 | bench_array_test.go:99: IndexOfNode=2
26 | BenchmarkIndexOfSelection 100000000 12.1 ns/op
27 | --- BENCH: BenchmarkIndexOfSelection
28 | bench_array_test.go:111: IndexOfSelection=2
29 | bench_array_test.go:111: IndexOfSelection=2
30 | bench_array_test.go:111: IndexOfSelection=2
31 | bench_array_test.go:111: IndexOfSelection=2
32 | bench_array_test.go:111: IndexOfSelection=2
33 | BenchmarkMetalReviewExample 5000 327144 ns/op
34 | --- BENCH: BenchmarkMetalReviewExample
35 | bench_example_test.go:40: Review 0: Midnight - Complete and Total Hell (8.5).
36 | Review 1: Over Your Threshold - Facticity (6.0).
37 | Review 2: Nuclear Death Terror - Chaos Reigns (7.5).
38 | Review 3: Evoken - Atra Mors (9.5).
39 |
40 | bench_example_test.go:41: MetalReviewExample=10
41 | bench_example_test.go:40: Review 0: Midnight - Complete and Total Hell (8.5).
42 | Review 1: Over Your Threshold - Facticity (6.0).
43 | Review 2: Nuclear Death Terror - Chaos Reigns (7.5).
44 | Review 3: Evoken - Atra Mors (9.5).
45 | ... [output truncated]
46 | BenchmarkAdd 50000 52945 ns/op
47 | --- BENCH: BenchmarkAdd
48 | bench_expand_test.go:20: Add=43
49 | bench_expand_test.go:20: Add=43
50 | bench_expand_test.go:20: Add=43
51 | bench_expand_test.go:20: Add=43
52 | BenchmarkAddSelection 10000000 205 ns/op
53 | --- BENCH: BenchmarkAddSelection
54 | bench_expand_test.go:37: AddSelection=43
55 | bench_expand_test.go:37: AddSelection=43
56 | bench_expand_test.go:37: AddSelection=43
57 | bench_expand_test.go:37: AddSelection=43
58 | bench_expand_test.go:37: AddSelection=43
59 | BenchmarkAddNodes 10000000 203 ns/op
60 | --- BENCH: BenchmarkAddNodes
61 | bench_expand_test.go:55: AddNodes=43
62 | bench_expand_test.go:55: AddNodes=43
63 | bench_expand_test.go:55: AddNodes=43
64 | bench_expand_test.go:55: AddNodes=43
65 | bench_expand_test.go:55: AddNodes=43
66 | BenchmarkAndSelf 1000000 2639 ns/op
67 | --- BENCH: BenchmarkAndSelf
68 | bench_expand_test.go:71: AndSelf=44
69 | bench_expand_test.go:71: AndSelf=44
70 | bench_expand_test.go:71: AndSelf=44
71 | bench_expand_test.go:71: AndSelf=44
72 | BenchmarkFilter 50000 30182 ns/op
73 | --- BENCH: BenchmarkFilter
74 | bench_filter_test.go:20: Filter=13
75 | bench_filter_test.go:20: Filter=13
76 | bench_filter_test.go:20: Filter=13
77 | bench_filter_test.go:20: Filter=13
78 | BenchmarkNot 50000 34855 ns/op
79 | --- BENCH: BenchmarkNot
80 | bench_filter_test.go:36: Not=371
81 | bench_filter_test.go:36: Not=371
82 | bench_filter_test.go:36: Not=371
83 | bench_filter_test.go:36: Not=371
84 | BenchmarkFilterFunction 50000 66052 ns/op
85 | --- BENCH: BenchmarkFilterFunction
86 | bench_filter_test.go:55: FilterFunction=112
87 | bench_filter_test.go:55: FilterFunction=112
88 | bench_filter_test.go:55: FilterFunction=112
89 | bench_filter_test.go:55: FilterFunction=112
90 | BenchmarkNotFunction 50000 69721 ns/op
91 | --- BENCH: BenchmarkNotFunction
92 | bench_filter_test.go:74: NotFunction=261
93 | bench_filter_test.go:74: NotFunction=261
94 | bench_filter_test.go:74: NotFunction=261
95 | bench_filter_test.go:74: NotFunction=261
96 | BenchmarkFilterNodes 50000 66077 ns/op
97 | --- BENCH: BenchmarkFilterNodes
98 | bench_filter_test.go:92: FilterNodes=2
99 | bench_filter_test.go:92: FilterNodes=2
100 | bench_filter_test.go:92: FilterNodes=2
101 | bench_filter_test.go:92: FilterNodes=2
102 | BenchmarkNotNodes 20000 80021 ns/op
103 | --- BENCH: BenchmarkNotNodes
104 | bench_filter_test.go:110: NotNodes=360
105 | bench_filter_test.go:110: NotNodes=360
106 | bench_filter_test.go:110: NotNodes=360
107 | bench_filter_test.go:110: NotNodes=360
108 | BenchmarkFilterSelection 50000 66256 ns/op
109 | --- BENCH: BenchmarkFilterSelection
110 | bench_filter_test.go:127: FilterSelection=2
111 | bench_filter_test.go:127: FilterSelection=2
112 | bench_filter_test.go:127: FilterSelection=2
113 | bench_filter_test.go:127: FilterSelection=2
114 | BenchmarkNotSelection 20000 79568 ns/op
115 | --- BENCH: BenchmarkNotSelection
116 | bench_filter_test.go:144: NotSelection=360
117 | bench_filter_test.go:144: NotSelection=360
118 | bench_filter_test.go:144: NotSelection=360
119 | bench_filter_test.go:144: NotSelection=360
120 | BenchmarkHas 5000 569441 ns/op
121 | --- BENCH: BenchmarkHas
122 | bench_filter_test.go:160: Has=13
123 | bench_filter_test.go:160: Has=13
124 | bench_filter_test.go:160: Has=13
125 | BenchmarkHasNodes 10000 230585 ns/op
126 | --- BENCH: BenchmarkHasNodes
127 | bench_filter_test.go:178: HasNodes=15
128 | bench_filter_test.go:178: HasNodes=15
129 | bench_filter_test.go:178: HasNodes=15
130 | BenchmarkHasSelection 10000 231470 ns/op
131 | --- BENCH: BenchmarkHasSelection
132 | bench_filter_test.go:195: HasSelection=15
133 | bench_filter_test.go:195: HasSelection=15
134 | bench_filter_test.go:195: HasSelection=15
135 | BenchmarkEnd 500000000 4.65 ns/op
136 | --- BENCH: BenchmarkEnd
137 | bench_filter_test.go:211: End=373
138 | bench_filter_test.go:211: End=373
139 | bench_filter_test.go:211: End=373
140 | bench_filter_test.go:211: End=373
141 | bench_filter_test.go:211: End=373
142 | bench_filter_test.go:211: End=373
143 | BenchmarkEach 200000 9558 ns/op
144 | --- BENCH: BenchmarkEach
145 | bench_iteration_test.go:22: Each=59
146 | bench_iteration_test.go:22: Each=59
147 | bench_iteration_test.go:22: Each=59
148 | bench_iteration_test.go:22: Each=59
149 | BenchmarkMap 100000 16809 ns/op
150 | --- BENCH: BenchmarkMap
151 | bench_iteration_test.go:41: Map=59
152 | bench_iteration_test.go:41: Map=59
153 | bench_iteration_test.go:41: Map=59
154 | bench_iteration_test.go:41: Map=59
155 | BenchmarkAttr 50000000 37.5 ns/op
156 | --- BENCH: BenchmarkAttr
157 | bench_property_test.go:16: Attr=firstHeading
158 | bench_property_test.go:16: Attr=firstHeading
159 | bench_property_test.go:16: Attr=firstHeading
160 | bench_property_test.go:16: Attr=firstHeading
161 | bench_property_test.go:16: Attr=firstHeading
162 | BenchmarkText 100000 18583 ns/op
163 | BenchmarkLength 2000000000 0.80 ns/op
164 | --- BENCH: BenchmarkLength
165 | bench_property_test.go:37: Length=14
166 | bench_property_test.go:37: Length=14
167 | bench_property_test.go:37: Length=14
168 | bench_property_test.go:37: Length=14
169 | bench_property_test.go:37: Length=14
170 | bench_property_test.go:37: Length=14
171 | BenchmarkHtml 5000000 666 ns/op
172 | BenchmarkIs 50000 34328 ns/op
173 | --- BENCH: BenchmarkIs
174 | bench_query_test.go:16: Is=true
175 | bench_query_test.go:16: Is=true
176 | bench_query_test.go:16: Is=true
177 | bench_query_test.go:16: Is=true
178 | BenchmarkIsPositional 50000 32423 ns/op
179 | --- BENCH: BenchmarkIsPositional
180 | bench_query_test.go:28: IsPositional=true
181 | bench_query_test.go:28: IsPositional=true
182 | bench_query_test.go:28: IsPositional=true
183 | bench_query_test.go:28: IsPositional=true
184 | BenchmarkIsFunction 1000000 2707 ns/op
185 | --- BENCH: BenchmarkIsFunction
186 | bench_query_test.go:43: IsFunction=true
187 | bench_query_test.go:43: IsFunction=true
188 | bench_query_test.go:43: IsFunction=true
189 | bench_query_test.go:43: IsFunction=true
190 | BenchmarkIsSelection 50000 66976 ns/op
191 | --- BENCH: BenchmarkIsSelection
192 | bench_query_test.go:56: IsSelection=true
193 | bench_query_test.go:56: IsSelection=true
194 | bench_query_test.go:56: IsSelection=true
195 | bench_query_test.go:56: IsSelection=true
196 | BenchmarkIsNodes 50000 66740 ns/op
197 | --- BENCH: BenchmarkIsNodes
198 | bench_query_test.go:70: IsNodes=true
199 | bench_query_test.go:70: IsNodes=true
200 | bench_query_test.go:70: IsNodes=true
201 | bench_query_test.go:70: IsNodes=true
202 | BenchmarkHasClass 5000 701722 ns/op
203 | --- BENCH: BenchmarkHasClass
204 | bench_query_test.go:82: HasClass=true
205 | bench_query_test.go:82: HasClass=true
206 | bench_query_test.go:82: HasClass=true
207 | BenchmarkContains 100000000 11.9 ns/op
208 | --- BENCH: BenchmarkContains
209 | bench_query_test.go:96: Contains=true
210 | bench_query_test.go:96: Contains=true
211 | bench_query_test.go:96: Contains=true
212 | bench_query_test.go:96: Contains=true
213 | bench_query_test.go:96: Contains=true
214 | BenchmarkFind 50000 55444 ns/op
215 | --- BENCH: BenchmarkFind
216 | bench_traversal_test.go:18: Find=41
217 | bench_traversal_test.go:18: Find=41
218 | bench_traversal_test.go:18: Find=41
219 | bench_traversal_test.go:18: Find=41
220 | BenchmarkFindWithinSelection 10000 127984 ns/op
221 | --- BENCH: BenchmarkFindWithinSelection
222 | bench_traversal_test.go:34: FindWithinSelection=39
223 | bench_traversal_test.go:34: FindWithinSelection=39
224 | bench_traversal_test.go:34: FindWithinSelection=39
225 | BenchmarkFindSelection 5000 355944 ns/op
226 | --- BENCH: BenchmarkFindSelection
227 | bench_traversal_test.go:51: FindSelection=73
228 | bench_traversal_test.go:51: FindSelection=73
229 | bench_traversal_test.go:51: FindSelection=73
230 | BenchmarkFindNodes 5000 355596 ns/op
231 | --- BENCH: BenchmarkFindNodes
232 | bench_traversal_test.go:69: FindNodes=73
233 | bench_traversal_test.go:69: FindNodes=73
234 | bench_traversal_test.go:69: FindNodes=73
235 | BenchmarkContents 500000 5656 ns/op
236 | --- BENCH: BenchmarkContents
237 | bench_traversal_test.go:85: Contents=16
238 | bench_traversal_test.go:85: Contents=16
239 | bench_traversal_test.go:85: Contents=16
240 | bench_traversal_test.go:85: Contents=16
241 | BenchmarkContentsFiltered 200000 9007 ns/op
242 | --- BENCH: BenchmarkContentsFiltered
243 | bench_traversal_test.go:101: ContentsFiltered=1
244 | bench_traversal_test.go:101: ContentsFiltered=1
245 | bench_traversal_test.go:101: ContentsFiltered=1
246 | bench_traversal_test.go:101: ContentsFiltered=1
247 | BenchmarkChildren 1000000 1237 ns/op
248 | --- BENCH: BenchmarkChildren
249 | bench_traversal_test.go:117: Children=2
250 | bench_traversal_test.go:117: Children=2
251 | bench_traversal_test.go:117: Children=2
252 | bench_traversal_test.go:117: Children=2
253 | BenchmarkChildrenFiltered 500000 5613 ns/op
254 | --- BENCH: BenchmarkChildrenFiltered
255 | bench_traversal_test.go:133: ChildrenFiltered=2
256 | bench_traversal_test.go:133: ChildrenFiltered=2
257 | bench_traversal_test.go:133: ChildrenFiltered=2
258 | bench_traversal_test.go:133: ChildrenFiltered=2
259 | BenchmarkParent 50000 47026 ns/op
260 | --- BENCH: BenchmarkParent
261 | bench_traversal_test.go:149: Parent=55
262 | bench_traversal_test.go:149: Parent=55
263 | bench_traversal_test.go:149: Parent=55
264 | bench_traversal_test.go:149: Parent=55
265 | BenchmarkParentFiltered 50000 51438 ns/op
266 | --- BENCH: BenchmarkParentFiltered
267 | bench_traversal_test.go:165: ParentFiltered=4
268 | bench_traversal_test.go:165: ParentFiltered=4
269 | bench_traversal_test.go:165: ParentFiltered=4
270 | bench_traversal_test.go:165: ParentFiltered=4
271 | BenchmarkParents 20000 91820 ns/op
272 | --- BENCH: BenchmarkParents
273 | bench_traversal_test.go:181: Parents=73
274 | bench_traversal_test.go:181: Parents=73
275 | bench_traversal_test.go:181: Parents=73
276 | bench_traversal_test.go:181: Parents=73
277 | BenchmarkParentsFiltered 20000 95156 ns/op
278 | --- BENCH: BenchmarkParentsFiltered
279 | bench_traversal_test.go:197: ParentsFiltered=18
280 | bench_traversal_test.go:197: ParentsFiltered=18
281 | bench_traversal_test.go:197: ParentsFiltered=18
282 | bench_traversal_test.go:197: ParentsFiltered=18
283 | BenchmarkParentsUntil 10000 134383 ns/op
284 | --- BENCH: BenchmarkParentsUntil
285 | bench_traversal_test.go:213: ParentsUntil=52
286 | bench_traversal_test.go:213: ParentsUntil=52
287 | bench_traversal_test.go:213: ParentsUntil=52
288 | BenchmarkParentsUntilSelection 10000 235456 ns/op
289 | --- BENCH: BenchmarkParentsUntilSelection
290 | bench_traversal_test.go:230: ParentsUntilSelection=70
291 | bench_traversal_test.go:230: ParentsUntilSelection=70
292 | bench_traversal_test.go:230: ParentsUntilSelection=70
293 | BenchmarkParentsUntilNodes 10000 235936 ns/op
294 | --- BENCH: BenchmarkParentsUntilNodes
295 | bench_traversal_test.go:248: ParentsUntilNodes=70
296 | bench_traversal_test.go:248: ParentsUntilNodes=70
297 | bench_traversal_test.go:248: ParentsUntilNodes=70
298 | BenchmarkParentsFilteredUntil 50000 32451 ns/op
299 | --- BENCH: BenchmarkParentsFilteredUntil
300 | bench_traversal_test.go:264: ParentsFilteredUntil=2
301 | bench_traversal_test.go:264: ParentsFilteredUntil=2
302 | bench_traversal_test.go:264: ParentsFilteredUntil=2
303 | bench_traversal_test.go:264: ParentsFilteredUntil=2
304 | BenchmarkParentsFilteredUntilSelection 50000 30570 ns/op
305 | --- BENCH: BenchmarkParentsFilteredUntilSelection
306 | bench_traversal_test.go:281: ParentsFilteredUntilSelection=2
307 | bench_traversal_test.go:281: ParentsFilteredUntilSelection=2
308 | bench_traversal_test.go:281: ParentsFilteredUntilSelection=2
309 | bench_traversal_test.go:281: ParentsFilteredUntilSelection=2
310 | BenchmarkParentsFilteredUntilNodes 50000 30729 ns/op
311 | --- BENCH: BenchmarkParentsFilteredUntilNodes
312 | bench_traversal_test.go:299: ParentsFilteredUntilNodes=2
313 | bench_traversal_test.go:299: ParentsFilteredUntilNodes=2
314 | bench_traversal_test.go:299: ParentsFilteredUntilNodes=2
315 | bench_traversal_test.go:299: ParentsFilteredUntilNodes=2
316 | BenchmarkSiblings 10000 106704 ns/op
317 | --- BENCH: BenchmarkSiblings
318 | bench_traversal_test.go:315: Siblings=293
319 | bench_traversal_test.go:315: Siblings=293
320 | bench_traversal_test.go:315: Siblings=293
321 | BenchmarkSiblingsFiltered 10000 115592 ns/op
322 | --- BENCH: BenchmarkSiblingsFiltered
323 | bench_traversal_test.go:331: SiblingsFiltered=46
324 | bench_traversal_test.go:331: SiblingsFiltered=46
325 | bench_traversal_test.go:331: SiblingsFiltered=46
326 | BenchmarkNext 50000 54449 ns/op
327 | --- BENCH: BenchmarkNext
328 | bench_traversal_test.go:347: Next=49
329 | bench_traversal_test.go:347: Next=49
330 | bench_traversal_test.go:347: Next=49
331 | bench_traversal_test.go:347: Next=49
332 | BenchmarkNextFiltered 50000 58503 ns/op
333 | --- BENCH: BenchmarkNextFiltered
334 | bench_traversal_test.go:363: NextFiltered=6
335 | bench_traversal_test.go:363: NextFiltered=6
336 | bench_traversal_test.go:363: NextFiltered=6
337 | bench_traversal_test.go:363: NextFiltered=6
338 | BenchmarkNextAll 20000 77698 ns/op
339 | --- BENCH: BenchmarkNextAll
340 | bench_traversal_test.go:379: NextAll=234
341 | bench_traversal_test.go:379: NextAll=234
342 | bench_traversal_test.go:379: NextAll=234
343 | bench_traversal_test.go:379: NextAll=234
344 | BenchmarkNextAllFiltered 20000 85034 ns/op
345 | --- BENCH: BenchmarkNextAllFiltered
346 | bench_traversal_test.go:395: NextAllFiltered=33
347 | bench_traversal_test.go:395: NextAllFiltered=33
348 | bench_traversal_test.go:395: NextAllFiltered=33
349 | bench_traversal_test.go:395: NextAllFiltered=33
350 | BenchmarkPrev 50000 56458 ns/op
351 | --- BENCH: BenchmarkPrev
352 | bench_traversal_test.go:411: Prev=49
353 | bench_traversal_test.go:411: Prev=49
354 | bench_traversal_test.go:411: Prev=49
355 | bench_traversal_test.go:411: Prev=49
356 | BenchmarkPrevFiltered 50000 60163 ns/op
357 | --- BENCH: BenchmarkPrevFiltered
358 | bench_traversal_test.go:429: PrevFiltered=7
359 | bench_traversal_test.go:429: PrevFiltered=7
360 | bench_traversal_test.go:429: PrevFiltered=7
361 | bench_traversal_test.go:429: PrevFiltered=7
362 | BenchmarkPrevAll 50000 47679 ns/op
363 | --- BENCH: BenchmarkPrevAll
364 | bench_traversal_test.go:445: PrevAll=78
365 | bench_traversal_test.go:445: PrevAll=78
366 | bench_traversal_test.go:445: PrevAll=78
367 | bench_traversal_test.go:445: PrevAll=78
368 | BenchmarkPrevAllFiltered 50000 51563 ns/op
369 | --- BENCH: BenchmarkPrevAllFiltered
370 | bench_traversal_test.go:461: PrevAllFiltered=6
371 | bench_traversal_test.go:461: PrevAllFiltered=6
372 | bench_traversal_test.go:461: PrevAllFiltered=6
373 | bench_traversal_test.go:461: PrevAllFiltered=6
374 | BenchmarkNextUntil 10000 213998 ns/op
375 | --- BENCH: BenchmarkNextUntil
376 | bench_traversal_test.go:477: NextUntil=84
377 | bench_traversal_test.go:477: NextUntil=84
378 | bench_traversal_test.go:477: NextUntil=84
379 | BenchmarkNextUntilSelection 10000 140720 ns/op
380 | --- BENCH: BenchmarkNextUntilSelection
381 | bench_traversal_test.go:494: NextUntilSelection=42
382 | bench_traversal_test.go:494: NextUntilSelection=42
383 | bench_traversal_test.go:494: NextUntilSelection=42
384 | BenchmarkNextUntilNodes 20000 90702 ns/op
385 | --- BENCH: BenchmarkNextUntilNodes
386 | bench_traversal_test.go:512: NextUntilNodes=12
387 | bench_traversal_test.go:512: NextUntilNodes=12
388 | bench_traversal_test.go:512: NextUntilNodes=12
389 | bench_traversal_test.go:512: NextUntilNodes=12
390 | BenchmarkPrevUntil 5000 456039 ns/op
391 | --- BENCH: BenchmarkPrevUntil
392 | bench_traversal_test.go:528: PrevUntil=238
393 | bench_traversal_test.go:528: PrevUntil=238
394 | bench_traversal_test.go:528: PrevUntil=238
395 | BenchmarkPrevUntilSelection 10000 167944 ns/op
396 | --- BENCH: BenchmarkPrevUntilSelection
397 | bench_traversal_test.go:545: PrevUntilSelection=49
398 | bench_traversal_test.go:545: PrevUntilSelection=49
399 | bench_traversal_test.go:545: PrevUntilSelection=49
400 | BenchmarkPrevUntilNodes 20000 82059 ns/op
401 | --- BENCH: BenchmarkPrevUntilNodes
402 | bench_traversal_test.go:563: PrevUntilNodes=11
403 | bench_traversal_test.go:563: PrevUntilNodes=11
404 | bench_traversal_test.go:563: PrevUntilNodes=11
405 | bench_traversal_test.go:563: PrevUntilNodes=11
406 | BenchmarkNextFilteredUntil 10000 150883 ns/op
407 | --- BENCH: BenchmarkNextFilteredUntil
408 | bench_traversal_test.go:579: NextFilteredUntil=22
409 | bench_traversal_test.go:579: NextFilteredUntil=22
410 | bench_traversal_test.go:579: NextFilteredUntil=22
411 | BenchmarkNextFilteredUntilSelection 10000 146578 ns/op
412 | --- BENCH: BenchmarkNextFilteredUntilSelection
413 | bench_traversal_test.go:596: NextFilteredUntilSelection=22
414 | bench_traversal_test.go:596: NextFilteredUntilSelection=22
415 | bench_traversal_test.go:596: NextFilteredUntilSelection=22
416 | BenchmarkNextFilteredUntilNodes 10000 148284 ns/op
417 | --- BENCH: BenchmarkNextFilteredUntilNodes
418 | bench_traversal_test.go:614: NextFilteredUntilNodes=22
419 | bench_traversal_test.go:614: NextFilteredUntilNodes=22
420 | bench_traversal_test.go:614: NextFilteredUntilNodes=22
421 | BenchmarkPrevFilteredUntil 10000 154303 ns/op
422 | --- BENCH: BenchmarkPrevFilteredUntil
423 | bench_traversal_test.go:630: PrevFilteredUntil=20
424 | bench_traversal_test.go:630: PrevFilteredUntil=20
425 | bench_traversal_test.go:630: PrevFilteredUntil=20
426 | BenchmarkPrevFilteredUntilSelection 10000 149062 ns/op
427 | --- BENCH: BenchmarkPrevFilteredUntilSelection
428 | bench_traversal_test.go:647: PrevFilteredUntilSelection=20
429 | bench_traversal_test.go:647: PrevFilteredUntilSelection=20
430 | bench_traversal_test.go:647: PrevFilteredUntilSelection=20
431 | BenchmarkPrevFilteredUntilNodes 10000 150584 ns/op
432 | --- BENCH: BenchmarkPrevFilteredUntilNodes
433 | bench_traversal_test.go:665: PrevFilteredUntilNodes=20
434 | bench_traversal_test.go:665: PrevFilteredUntilNodes=20
435 | bench_traversal_test.go:665: PrevFilteredUntilNodes=20
436 | ok github.com/PuerkitoBio/goquery 188.326s
437 |
--------------------------------------------------------------------------------
/bench/v1.0.0-go1.7:
--------------------------------------------------------------------------------
1 | BenchmarkFirst-4 30000000 50.7 ns/op 48 B/op 1 allocs/op
2 | BenchmarkLast-4 30000000 50.9 ns/op 48 B/op 1 allocs/op
3 | BenchmarkEq-4 30000000 55.7 ns/op 48 B/op 1 allocs/op
4 | BenchmarkSlice-4 500000000 3.45 ns/op 0 B/op 0 allocs/op
5 | BenchmarkGet-4 2000000000 1.68 ns/op 0 B/op 0 allocs/op
6 | BenchmarkIndex-4 3000000 541 ns/op 248 B/op 10 allocs/op
7 | BenchmarkIndexSelector-4 200000 10749 ns/op 2464 B/op 17 allocs/op
8 | BenchmarkIndexOfNode-4 200000000 6.47 ns/op 0 B/op 0 allocs/op
9 | BenchmarkIndexOfSelection-4 200000000 7.27 ns/op 0 B/op 0 allocs/op
10 | BenchmarkMetalReviewExample-4 10000 138426 ns/op 12240 B/op 319 allocs/op
11 | BenchmarkAdd-4 200000 10192 ns/op 208 B/op 9 allocs/op
12 | BenchmarkAddSelection-4 10000000 158 ns/op 48 B/op 1 allocs/op
13 | BenchmarkAddNodes-4 10000000 156 ns/op 48 B/op 1 allocs/op
14 | BenchmarkAndSelf-4 1000000 1588 ns/op 1008 B/op 5 allocs/op
15 | BenchmarkFilter-4 100000 20427 ns/op 360 B/op 8 allocs/op
16 | BenchmarkNot-4 100000 23508 ns/op 136 B/op 5 allocs/op
17 | BenchmarkFilterFunction-4 50000 34178 ns/op 22976 B/op 755 allocs/op
18 | BenchmarkNotFunction-4 50000 38173 ns/op 29120 B/op 757 allocs/op
19 | BenchmarkFilterNodes-4 50000 34001 ns/op 20960 B/op 749 allocs/op
20 | BenchmarkNotNodes-4 30000 40344 ns/op 29120 B/op 757 allocs/op
21 | BenchmarkFilterSelection-4 50000 33308 ns/op 20960 B/op 749 allocs/op
22 | BenchmarkNotSelection-4 30000 40748 ns/op 29120 B/op 757 allocs/op
23 | BenchmarkHas-4 5000 263346 ns/op 1816 B/op 48 allocs/op
24 | BenchmarkHasNodes-4 10000 160840 ns/op 21184 B/op 752 allocs/op
25 | BenchmarkHasSelection-4 10000 165410 ns/op 21184 B/op 752 allocs/op
26 | BenchmarkEnd-4 2000000000 1.01 ns/op 0 B/op 0 allocs/op
27 | BenchmarkEach-4 300000 4664 ns/op 3304 B/op 118 allocs/op
28 | BenchmarkMap-4 200000 8286 ns/op 5572 B/op 184 allocs/op
29 | BenchmarkEachWithBreak-4 2000000 806 ns/op 560 B/op 20 allocs/op
30 | BenchmarkAttr-4 100000000 21.6 ns/op 0 B/op 0 allocs/op
31 | BenchmarkText-4 200000 8909 ns/op 7536 B/op 110 allocs/op
32 | BenchmarkLength-4 2000000000 0.34 ns/op 0 B/op 0 allocs/op
33 | BenchmarkHtml-4 3000000 422 ns/op 120 B/op 2 allocs/op
34 | BenchmarkIs-4 100000 22615 ns/op 88 B/op 4 allocs/op
35 | BenchmarkIsPositional-4 50000 26655 ns/op 1112 B/op 10 allocs/op
36 | BenchmarkIsFunction-4 1000000 1208 ns/op 784 B/op 28 allocs/op
37 | BenchmarkIsSelection-4 50000 33497 ns/op 20960 B/op 749 allocs/op
38 | BenchmarkIsNodes-4 50000 33572 ns/op 20960 B/op 749 allocs/op
39 | BenchmarkHasClass-4 10000 232802 ns/op 14944 B/op 976 allocs/op
40 | BenchmarkContains-4 200000000 7.33 ns/op 0 B/op 0 allocs/op
41 | BenchmarkFind-4 200000 10715 ns/op 2464 B/op 17 allocs/op
42 | BenchmarkFindWithinSelection-4 50000 35878 ns/op 2176 B/op 78 allocs/op
43 | BenchmarkFindSelection-4 10000 194356 ns/op 2672 B/op 82 allocs/op
44 | BenchmarkFindNodes-4 10000 195510 ns/op 2672 B/op 82 allocs/op
45 | BenchmarkContents-4 1000000 2252 ns/op 864 B/op 34 allocs/op
46 | BenchmarkContentsFiltered-4 500000 3015 ns/op 1016 B/op 39 allocs/op
47 | BenchmarkChildren-4 5000000 364 ns/op 152 B/op 7 allocs/op
48 | BenchmarkChildrenFiltered-4 1000000 2212 ns/op 352 B/op 15 allocs/op
49 | BenchmarkParent-4 50000 24643 ns/op 4048 B/op 381 allocs/op
50 | BenchmarkParentFiltered-4 50000 25967 ns/op 4248 B/op 388 allocs/op
51 | BenchmarkParents-4 30000 50000 ns/op 27776 B/op 830 allocs/op
52 | BenchmarkParentsFiltered-4 30000 53107 ns/op 28360 B/op 838 allocs/op
53 | BenchmarkParentsUntil-4 100000 22423 ns/op 10352 B/op 353 allocs/op
54 | BenchmarkParentsUntilSelection-4 20000 86925 ns/op 51144 B/op 1516 allocs/op
55 | BenchmarkParentsUntilNodes-4 20000 87597 ns/op 51144 B/op 1516 allocs/op
56 | BenchmarkParentsFilteredUntil-4 300000 5568 ns/op 2232 B/op 86 allocs/op
57 | BenchmarkParentsFilteredUntilSelection-4 200000 10966 ns/op 5440 B/op 190 allocs/op
58 | BenchmarkParentsFilteredUntilNodes-4 200000 10919 ns/op 5440 B/op 190 allocs/op
59 | BenchmarkSiblings-4 30000 46018 ns/op 15400 B/op 204 allocs/op
60 | BenchmarkSiblingsFiltered-4 30000 50566 ns/op 16496 B/op 213 allocs/op
61 | BenchmarkNext-4 200000 7921 ns/op 3216 B/op 112 allocs/op
62 | BenchmarkNextFiltered-4 200000 8804 ns/op 3416 B/op 118 allocs/op
63 | BenchmarkNextAll-4 50000 31098 ns/op 9912 B/op 138 allocs/op
64 | BenchmarkNextAllFiltered-4 50000 34677 ns/op 11008 B/op 147 allocs/op
65 | BenchmarkPrev-4 200000 7920 ns/op 3216 B/op 112 allocs/op
66 | BenchmarkPrevFiltered-4 200000 8913 ns/op 3416 B/op 118 allocs/op
67 | BenchmarkPrevAll-4 200000 10845 ns/op 4376 B/op 113 allocs/op
68 | BenchmarkPrevAllFiltered-4 100000 12030 ns/op 4576 B/op 119 allocs/op
69 | BenchmarkNextUntil-4 100000 19193 ns/op 5760 B/op 260 allocs/op
70 | BenchmarkNextUntilSelection-4 50000 34829 ns/op 18480 B/op 542 allocs/op
71 | BenchmarkNextUntilNodes-4 100000 14459 ns/op 7944 B/op 248 allocs/op
72 | BenchmarkPrevUntil-4 20000 66296 ns/op 12856 B/op 448 allocs/op
73 | BenchmarkPrevUntilSelection-4 30000 45037 ns/op 23432 B/op 689 allocs/op
74 | BenchmarkPrevUntilNodes-4 200000 11525 ns/op 6152 B/op 203 allocs/op
75 | BenchmarkNextFilteredUntil-4 100000 12940 ns/op 4512 B/op 173 allocs/op
76 | BenchmarkNextFilteredUntilSelection-4 50000 38924 ns/op 19160 B/op 567 allocs/op
77 | BenchmarkNextFilteredUntilNodes-4 50000 38528 ns/op 19160 B/op 567 allocs/op
78 | BenchmarkPrevFilteredUntil-4 100000 12980 ns/op 4664 B/op 175 allocs/op
79 | BenchmarkPrevFilteredUntilSelection-4 50000 39671 ns/op 19936 B/op 587 allocs/op
80 | BenchmarkPrevFilteredUntilNodes-4 50000 39484 ns/op 19936 B/op 587 allocs/op
81 | BenchmarkClosest-4 500000 3310 ns/op 160 B/op 8 allocs/op
82 | BenchmarkClosestSelection-4 5000000 361 ns/op 96 B/op 6 allocs/op
83 | BenchmarkClosestNodes-4 5000000 359 ns/op 96 B/op 6 allocs/op
84 | PASS
85 | ok github.com/PuerkitoBio/goquery 163.718s
86 |
--------------------------------------------------------------------------------
/bench/v1.0.1a-go1.7:
--------------------------------------------------------------------------------
1 | BenchmarkFirst-4 30000000 50.9 ns/op 48 B/op 1 allocs/op
2 | BenchmarkLast-4 30000000 50.0 ns/op 48 B/op 1 allocs/op
3 | BenchmarkEq-4 30000000 50.5 ns/op 48 B/op 1 allocs/op
4 | BenchmarkSlice-4 500000000 3.53 ns/op 0 B/op 0 allocs/op
5 | BenchmarkGet-4 2000000000 1.66 ns/op 0 B/op 0 allocs/op
6 | BenchmarkIndex-4 2000000 832 ns/op 248 B/op 10 allocs/op
7 | BenchmarkIndexSelector-4 100000 16073 ns/op 3839 B/op 21 allocs/op
8 | BenchmarkIndexOfNode-4 200000000 6.38 ns/op 0 B/op 0 allocs/op
9 | BenchmarkIndexOfSelection-4 200000000 7.14 ns/op 0 B/op 0 allocs/op
10 | BenchmarkMetalReviewExample-4 10000 140737 ns/op 12418 B/op 320 allocs/op
11 | BenchmarkAdd-4 100000 13162 ns/op 974 B/op 10 allocs/op
12 | BenchmarkAddSelection-4 500000 3160 ns/op 814 B/op 2 allocs/op
13 | BenchmarkAddNodes-4 500000 3159 ns/op 814 B/op 2 allocs/op
14 | BenchmarkAndSelf-4 200000 7423 ns/op 2404 B/op 9 allocs/op
15 | BenchmarkFilter-4 100000 19671 ns/op 360 B/op 8 allocs/op
16 | BenchmarkNot-4 100000 22577 ns/op 136 B/op 5 allocs/op
17 | BenchmarkFilterFunction-4 50000 33960 ns/op 22976 B/op 755 allocs/op
18 | BenchmarkNotFunction-4 50000 37909 ns/op 29120 B/op 757 allocs/op
19 | BenchmarkFilterNodes-4 50000 34196 ns/op 20960 B/op 749 allocs/op
20 | BenchmarkNotNodes-4 30000 40446 ns/op 29120 B/op 757 allocs/op
21 | BenchmarkFilterSelection-4 50000 33091 ns/op 20960 B/op 749 allocs/op
22 | BenchmarkNotSelection-4 30000 40609 ns/op 29120 B/op 757 allocs/op
23 | BenchmarkHas-4 5000 262936 ns/op 2371 B/op 50 allocs/op
24 | BenchmarkHasNodes-4 10000 148631 ns/op 21184 B/op 752 allocs/op
25 | BenchmarkHasSelection-4 10000 153117 ns/op 21184 B/op 752 allocs/op
26 | BenchmarkEnd-4 2000000000 1.02 ns/op 0 B/op 0 allocs/op
27 | BenchmarkEach-4 300000 4653 ns/op 3304 B/op 118 allocs/op
28 | BenchmarkMap-4 200000 8257 ns/op 5572 B/op 184 allocs/op
29 | BenchmarkEachWithBreak-4 2000000 806 ns/op 560 B/op 20 allocs/op
30 | BenchmarkAttr-4 100000000 22.0 ns/op 0 B/op 0 allocs/op
31 | BenchmarkText-4 200000 8913 ns/op 7536 B/op 110 allocs/op
32 | BenchmarkLength-4 2000000000 0.35 ns/op 0 B/op 0 allocs/op
33 | BenchmarkHtml-4 5000000 398 ns/op 120 B/op 2 allocs/op
34 | BenchmarkIs-4 100000 22392 ns/op 88 B/op 4 allocs/op
35 | BenchmarkIsPositional-4 50000 26259 ns/op 1112 B/op 10 allocs/op
36 | BenchmarkIsFunction-4 1000000 1212 ns/op 784 B/op 28 allocs/op
37 | BenchmarkIsSelection-4 50000 33222 ns/op 20960 B/op 749 allocs/op
38 | BenchmarkIsNodes-4 50000 33408 ns/op 20960 B/op 749 allocs/op
39 | BenchmarkHasClass-4 10000 233208 ns/op 14944 B/op 976 allocs/op
40 | BenchmarkContains-4 200000000 7.57 ns/op 0 B/op 0 allocs/op
41 | BenchmarkFind-4 100000 16121 ns/op 3839 B/op 21 allocs/op
42 | BenchmarkFindWithinSelection-4 20000 68019 ns/op 11521 B/op 97 allocs/op
43 | BenchmarkFindSelection-4 5000 387582 ns/op 59787 B/op 176 allocs/op
44 | BenchmarkFindNodes-4 5000 389246 ns/op 59797 B/op 176 allocs/op
45 | BenchmarkContents-4 200000 11475 ns/op 2878 B/op 42 allocs/op
46 | BenchmarkContentsFiltered-4 200000 11222 ns/op 2498 B/op 46 allocs/op
47 | BenchmarkChildren-4 2000000 650 ns/op 152 B/op 7 allocs/op
48 | BenchmarkChildrenFiltered-4 500000 2568 ns/op 352 B/op 15 allocs/op
49 | BenchmarkParent-4 2000 702513 ns/op 194478 B/op 828 allocs/op
50 | BenchmarkParentFiltered-4 2000 690778 ns/op 194658 B/op 835 allocs/op
51 | BenchmarkParents-4 10000 124855 ns/op 49869 B/op 868 allocs/op
52 | BenchmarkParentsFiltered-4 10000 128535 ns/op 50456 B/op 876 allocs/op
53 | BenchmarkParentsUntil-4 20000 72982 ns/op 23802 B/op 388 allocs/op
54 | BenchmarkParentsUntilSelection-4 10000 156099 ns/op 72453 B/op 1549 allocs/op
55 | BenchmarkParentsUntilNodes-4 10000 156610 ns/op 72455 B/op 1549 allocs/op
56 | BenchmarkParentsFilteredUntil-4 100000 15549 ns/op 4068 B/op 94 allocs/op
57 | BenchmarkParentsFilteredUntilSelection-4 100000 20564 ns/op 7276 B/op 198 allocs/op
58 | BenchmarkParentsFilteredUntilNodes-4 100000 20635 ns/op 7276 B/op 198 allocs/op
59 | BenchmarkSiblings-4 3000 565114 ns/op 205910 B/op 336 allocs/op
60 | BenchmarkSiblingsFiltered-4 3000 580264 ns/op 206993 B/op 345 allocs/op
61 | BenchmarkNext-4 20000 93177 ns/op 26810 B/op 169 allocs/op
62 | BenchmarkNextFiltered-4 20000 94171 ns/op 27013 B/op 175 allocs/op
63 | BenchmarkNextAll-4 5000 270320 ns/op 89289 B/op 237 allocs/op
64 | BenchmarkNextAllFiltered-4 5000 275283 ns/op 90375 B/op 246 allocs/op
65 | BenchmarkPrev-4 20000 92777 ns/op 26810 B/op 169 allocs/op
66 | BenchmarkPrevFiltered-4 20000 95577 ns/op 27007 B/op 175 allocs/op
67 | BenchmarkPrevAll-4 20000 86339 ns/op 27515 B/op 151 allocs/op
68 | BenchmarkPrevAllFiltered-4 20000 87759 ns/op 27715 B/op 157 allocs/op
69 | BenchmarkNextUntil-4 10000 163930 ns/op 48541 B/op 330 allocs/op
70 | BenchmarkNextUntilSelection-4 30000 56382 ns/op 23880 B/op 556 allocs/op
71 | BenchmarkNextUntilNodes-4 100000 18883 ns/op 8703 B/op 252 allocs/op
72 | BenchmarkPrevUntil-4 3000 484668 ns/op 145402 B/op 611 allocs/op
73 | BenchmarkPrevUntilSelection-4 20000 72125 ns/op 28865 B/op 705 allocs/op
74 | BenchmarkPrevUntilNodes-4 100000 14722 ns/op 6510 B/op 205 allocs/op
75 | BenchmarkNextFilteredUntil-4 50000 39006 ns/op 10990 B/op 192 allocs/op
76 | BenchmarkNextFilteredUntilSelection-4 20000 66048 ns/op 25641 B/op 586 allocs/op
77 | BenchmarkNextFilteredUntilNodes-4 20000 65314 ns/op 25640 B/op 586 allocs/op
78 | BenchmarkPrevFilteredUntil-4 50000 33312 ns/op 9709 B/op 189 allocs/op
79 | BenchmarkPrevFilteredUntilSelection-4 20000 64197 ns/op 24981 B/op 601 allocs/op
80 | BenchmarkPrevFilteredUntilNodes-4 20000 64505 ns/op 24982 B/op 601 allocs/op
81 | BenchmarkClosest-4 500000 4065 ns/op 160 B/op 8 allocs/op
82 | BenchmarkClosestSelection-4 2000000 756 ns/op 96 B/op 6 allocs/op
83 | BenchmarkClosestNodes-4 2000000 753 ns/op 96 B/op 6 allocs/op
84 | PASS
85 | ok github.com/PuerkitoBio/goquery 162.053s
86 |
--------------------------------------------------------------------------------
/bench/v1.0.1b-go1.7:
--------------------------------------------------------------------------------
1 | BenchmarkFirst-4 30000000 51.8 ns/op 48 B/op 1 allocs/op
2 | BenchmarkLast-4 30000000 50.1 ns/op 48 B/op 1 allocs/op
3 | BenchmarkEq-4 30000000 51.4 ns/op 48 B/op 1 allocs/op
4 | BenchmarkSlice-4 500000000 3.52 ns/op 0 B/op 0 allocs/op
5 | BenchmarkGet-4 2000000000 1.65 ns/op 0 B/op 0 allocs/op
6 | BenchmarkIndex-4 2000000 787 ns/op 248 B/op 10 allocs/op
7 | BenchmarkIndexSelector-4 100000 16952 ns/op 3839 B/op 21 allocs/op
8 | BenchmarkIndexOfNode-4 200000000 6.42 ns/op 0 B/op 0 allocs/op
9 | BenchmarkIndexOfSelection-4 200000000 7.12 ns/op 0 B/op 0 allocs/op
10 | BenchmarkMetalReviewExample-4 10000 141994 ns/op 12418 B/op 320 allocs/op
11 | BenchmarkAdd-4 200000 10367 ns/op 208 B/op 9 allocs/op
12 | BenchmarkAddSelection-4 10000000 152 ns/op 48 B/op 1 allocs/op
13 | BenchmarkAddNodes-4 10000000 147 ns/op 48 B/op 1 allocs/op
14 | BenchmarkAndSelf-4 1000000 1647 ns/op 1008 B/op 5 allocs/op
15 | BenchmarkFilter-4 100000 19522 ns/op 360 B/op 8 allocs/op
16 | BenchmarkNot-4 100000 22546 ns/op 136 B/op 5 allocs/op
17 | BenchmarkFilterFunction-4 50000 35087 ns/op 22976 B/op 755 allocs/op
18 | BenchmarkNotFunction-4 50000 39123 ns/op 29120 B/op 757 allocs/op
19 | BenchmarkFilterNodes-4 50000 34890 ns/op 20960 B/op 749 allocs/op
20 | BenchmarkNotNodes-4 30000 41145 ns/op 29120 B/op 757 allocs/op
21 | BenchmarkFilterSelection-4 50000 33735 ns/op 20960 B/op 749 allocs/op
22 | BenchmarkNotSelection-4 30000 41334 ns/op 29120 B/op 757 allocs/op
23 | BenchmarkHas-4 5000 264058 ns/op 2370 B/op 50 allocs/op
24 | BenchmarkHasNodes-4 10000 151718 ns/op 21184 B/op 752 allocs/op
25 | BenchmarkHasSelection-4 10000 156955 ns/op 21184 B/op 752 allocs/op
26 | BenchmarkEnd-4 2000000000 1.01 ns/op 0 B/op 0 allocs/op
27 | BenchmarkEach-4 300000 4660 ns/op 3304 B/op 118 allocs/op
28 | BenchmarkMap-4 200000 8404 ns/op 5572 B/op 184 allocs/op
29 | BenchmarkEachWithBreak-4 2000000 806 ns/op 560 B/op 20 allocs/op
30 | BenchmarkAttr-4 100000000 21.6 ns/op 0 B/op 0 allocs/op
31 | BenchmarkText-4 200000 8911 ns/op 7536 B/op 110 allocs/op
32 | BenchmarkLength-4 2000000000 0.34 ns/op 0 B/op 0 allocs/op
33 | BenchmarkHtml-4 3000000 405 ns/op 120 B/op 2 allocs/op
34 | BenchmarkIs-4 100000 22228 ns/op 88 B/op 4 allocs/op
35 | BenchmarkIsPositional-4 50000 26469 ns/op 1112 B/op 10 allocs/op
36 | BenchmarkIsFunction-4 1000000 1240 ns/op 784 B/op 28 allocs/op
37 | BenchmarkIsSelection-4 50000 33709 ns/op 20960 B/op 749 allocs/op
38 | BenchmarkIsNodes-4 50000 33711 ns/op 20960 B/op 749 allocs/op
39 | BenchmarkHasClass-4 10000 236005 ns/op 14944 B/op 976 allocs/op
40 | BenchmarkContains-4 200000000 7.47 ns/op 0 B/op 0 allocs/op
41 | BenchmarkFind-4 100000 16075 ns/op 3839 B/op 21 allocs/op
42 | BenchmarkFindWithinSelection-4 30000 41418 ns/op 3539 B/op 82 allocs/op
43 | BenchmarkFindSelection-4 10000 209490 ns/op 5616 B/op 89 allocs/op
44 | BenchmarkFindNodes-4 10000 208206 ns/op 5614 B/op 89 allocs/op
45 | BenchmarkContents-4 300000 4751 ns/op 1420 B/op 36 allocs/op
46 | BenchmarkContentsFiltered-4 300000 5454 ns/op 1570 B/op 41 allocs/op
47 | BenchmarkChildren-4 3000000 527 ns/op 152 B/op 7 allocs/op
48 | BenchmarkChildrenFiltered-4 1000000 2484 ns/op 352 B/op 15 allocs/op
49 | BenchmarkParent-4 50000 34724 ns/op 6940 B/op 387 allocs/op
50 | BenchmarkParentFiltered-4 50000 35596 ns/op 7141 B/op 394 allocs/op
51 | BenchmarkParents-4 20000 62094 ns/op 30720 B/op 837 allocs/op
52 | BenchmarkParentsFiltered-4 20000 63223 ns/op 31304 B/op 845 allocs/op
53 | BenchmarkParentsUntil-4 50000 30391 ns/op 11828 B/op 358 allocs/op
54 | BenchmarkParentsUntilSelection-4 20000 99962 ns/op 54075 B/op 1523 allocs/op
55 | BenchmarkParentsUntilNodes-4 20000 98763 ns/op 54073 B/op 1523 allocs/op
56 | BenchmarkParentsFilteredUntil-4 200000 7982 ns/op 2787 B/op 88 allocs/op
57 | BenchmarkParentsFilteredUntilSelection-4 100000 13618 ns/op 5995 B/op 192 allocs/op
58 | BenchmarkParentsFilteredUntilNodes-4 100000 13639 ns/op 5994 B/op 192 allocs/op
59 | BenchmarkSiblings-4 20000 75287 ns/op 28453 B/op 225 allocs/op
60 | BenchmarkSiblingsFiltered-4 20000 80139 ns/op 29543 B/op 234 allocs/op
61 | BenchmarkNext-4 100000 14270 ns/op 4659 B/op 117 allocs/op
62 | BenchmarkNextFiltered-4 100000 15352 ns/op 4860 B/op 123 allocs/op
63 | BenchmarkNextAll-4 20000 60811 ns/op 22771 B/op 157 allocs/op
64 | BenchmarkNextAllFiltered-4 20000 69079 ns/op 23871 B/op 166 allocs/op
65 | BenchmarkPrev-4 100000 14417 ns/op 4659 B/op 117 allocs/op
66 | BenchmarkPrevFiltered-4 100000 15443 ns/op 4859 B/op 123 allocs/op
67 | BenchmarkPrevAll-4 100000 22008 ns/op 7346 B/op 120 allocs/op
68 | BenchmarkPrevAllFiltered-4 100000 23212 ns/op 7544 B/op 126 allocs/op
69 | BenchmarkNextUntil-4 50000 30589 ns/op 8767 B/op 267 allocs/op
70 | BenchmarkNextUntilSelection-4 30000 40875 ns/op 19862 B/op 546 allocs/op
71 | BenchmarkNextUntilNodes-4 100000 15987 ns/op 8134 B/op 249 allocs/op
72 | BenchmarkPrevUntil-4 20000 98799 ns/op 25727 B/op 467 allocs/op
73 | BenchmarkPrevUntilSelection-4 30000 51874 ns/op 24875 B/op 694 allocs/op
74 | BenchmarkPrevUntilNodes-4 100000 12901 ns/op 6334 B/op 204 allocs/op
75 | BenchmarkNextFilteredUntil-4 100000 19869 ns/op 5909 B/op 177 allocs/op
76 | BenchmarkNextFilteredUntilSelection-4 30000 45412 ns/op 20557 B/op 571 allocs/op
77 | BenchmarkNextFilteredUntilNodes-4 30000 45363 ns/op 20557 B/op 571 allocs/op
78 | BenchmarkPrevFilteredUntil-4 100000 19357 ns/op 6033 B/op 179 allocs/op
79 | BenchmarkPrevFilteredUntilSelection-4 30000 46396 ns/op 21305 B/op 591 allocs/op
80 | BenchmarkPrevFilteredUntilNodes-4 30000 46133 ns/op 21305 B/op 591 allocs/op
81 | BenchmarkClosest-4 500000 3448 ns/op 160 B/op 8 allocs/op
82 | BenchmarkClosestSelection-4 3000000 528 ns/op 96 B/op 6 allocs/op
83 | BenchmarkClosestNodes-4 3000000 523 ns/op 96 B/op 6 allocs/op
84 | PASS
85 | ok github.com/PuerkitoBio/goquery 162.012s
86 |
--------------------------------------------------------------------------------
/bench/v1.0.1c-go1.7:
--------------------------------------------------------------------------------
1 | BenchmarkFirst-4 30000000 51.7 ns/op 48 B/op 1 allocs/op
2 | BenchmarkLast-4 30000000 51.9 ns/op 48 B/op 1 allocs/op
3 | BenchmarkEq-4 30000000 50.0 ns/op 48 B/op 1 allocs/op
4 | BenchmarkSlice-4 500000000 3.47 ns/op 0 B/op 0 allocs/op
5 | BenchmarkGet-4 2000000000 1.68 ns/op 0 B/op 0 allocs/op
6 | BenchmarkIndex-4 2000000 804 ns/op 248 B/op 10 allocs/op
7 | BenchmarkIndexSelector-4 100000 16285 ns/op 3839 B/op 21 allocs/op
8 | BenchmarkIndexOfNode-4 200000000 6.50 ns/op 0 B/op 0 allocs/op
9 | BenchmarkIndexOfSelection-4 200000000 7.02 ns/op 0 B/op 0 allocs/op
10 | BenchmarkMetalReviewExample-4 10000 143160 ns/op 12417 B/op 320 allocs/op
11 | BenchmarkAdd-4 200000 10326 ns/op 208 B/op 9 allocs/op
12 | BenchmarkAddSelection-4 10000000 155 ns/op 48 B/op 1 allocs/op
13 | BenchmarkAddNodes-4 10000000 156 ns/op 48 B/op 1 allocs/op
14 | BenchmarkAddNodesBig-4 20000 94439 ns/op 21847 B/op 37 allocs/op
15 | BenchmarkAndSelf-4 1000000 1791 ns/op 1008 B/op 5 allocs/op
16 | BenchmarkFilter-4 100000 19470 ns/op 360 B/op 8 allocs/op
17 | BenchmarkNot-4 100000 22500 ns/op 136 B/op 5 allocs/op
18 | BenchmarkFilterFunction-4 50000 34578 ns/op 22976 B/op 755 allocs/op
19 | BenchmarkNotFunction-4 50000 38703 ns/op 29120 B/op 757 allocs/op
20 | BenchmarkFilterNodes-4 50000 34486 ns/op 20960 B/op 749 allocs/op
21 | BenchmarkNotNodes-4 30000 41094 ns/op 29120 B/op 757 allocs/op
22 | BenchmarkFilterSelection-4 50000 33623 ns/op 20960 B/op 749 allocs/op
23 | BenchmarkNotSelection-4 30000 41483 ns/op 29120 B/op 757 allocs/op
24 | BenchmarkHas-4 5000 266628 ns/op 2371 B/op 50 allocs/op
25 | BenchmarkHasNodes-4 10000 152617 ns/op 21184 B/op 752 allocs/op
26 | BenchmarkHasSelection-4 10000 156682 ns/op 21184 B/op 752 allocs/op
27 | BenchmarkEnd-4 2000000000 1.00 ns/op 0 B/op 0 allocs/op
28 | BenchmarkEach-4 300000 4712 ns/op 3304 B/op 118 allocs/op
29 | BenchmarkMap-4 200000 8434 ns/op 5572 B/op 184 allocs/op
30 | BenchmarkEachWithBreak-4 2000000 819 ns/op 560 B/op 20 allocs/op
31 | BenchmarkAttr-4 100000000 21.7 ns/op 0 B/op 0 allocs/op
32 | BenchmarkText-4 200000 9376 ns/op 7536 B/op 110 allocs/op
33 | BenchmarkLength-4 2000000000 0.35 ns/op 0 B/op 0 allocs/op
34 | BenchmarkHtml-4 5000000 401 ns/op 120 B/op 2 allocs/op
35 | BenchmarkIs-4 100000 22214 ns/op 88 B/op 4 allocs/op
36 | BenchmarkIsPositional-4 50000 26559 ns/op 1112 B/op 10 allocs/op
37 | BenchmarkIsFunction-4 1000000 1228 ns/op 784 B/op 28 allocs/op
38 | BenchmarkIsSelection-4 50000 33471 ns/op 20960 B/op 749 allocs/op
39 | BenchmarkIsNodes-4 50000 34461 ns/op 20960 B/op 749 allocs/op
40 | BenchmarkHasClass-4 10000 232429 ns/op 14944 B/op 976 allocs/op
41 | BenchmarkContains-4 200000000 7.62 ns/op 0 B/op 0 allocs/op
42 | BenchmarkFind-4 100000 16114 ns/op 3839 B/op 21 allocs/op
43 | BenchmarkFindWithinSelection-4 30000 42520 ns/op 3540 B/op 82 allocs/op
44 | BenchmarkFindSelection-4 10000 209801 ns/op 5615 B/op 89 allocs/op
45 | BenchmarkFindNodes-4 10000 209082 ns/op 5614 B/op 89 allocs/op
46 | BenchmarkContents-4 300000 4836 ns/op 1420 B/op 36 allocs/op
47 | BenchmarkContentsFiltered-4 200000 5495 ns/op 1570 B/op 41 allocs/op
48 | BenchmarkChildren-4 3000000 527 ns/op 152 B/op 7 allocs/op
49 | BenchmarkChildrenFiltered-4 500000 2499 ns/op 352 B/op 15 allocs/op
50 | BenchmarkParent-4 50000 34072 ns/op 6942 B/op 387 allocs/op
51 | BenchmarkParentFiltered-4 50000 36077 ns/op 7141 B/op 394 allocs/op
52 | BenchmarkParents-4 20000 64118 ns/op 30719 B/op 837 allocs/op
53 | BenchmarkParentsFiltered-4 20000 63432 ns/op 31303 B/op 845 allocs/op
54 | BenchmarkParentsUntil-4 50000 29589 ns/op 11829 B/op 358 allocs/op
55 | BenchmarkParentsUntilSelection-4 10000 101033 ns/op 54076 B/op 1523 allocs/op
56 | BenchmarkParentsUntilNodes-4 10000 100584 ns/op 54076 B/op 1523 allocs/op
57 | BenchmarkParentsFilteredUntil-4 200000 8061 ns/op 2787 B/op 88 allocs/op
58 | BenchmarkParentsFilteredUntilSelection-4 100000 13848 ns/op 5995 B/op 192 allocs/op
59 | BenchmarkParentsFilteredUntilNodes-4 100000 13766 ns/op 5995 B/op 192 allocs/op
60 | BenchmarkSiblings-4 20000 75135 ns/op 28453 B/op 225 allocs/op
61 | BenchmarkSiblingsFiltered-4 20000 80532 ns/op 29544 B/op 234 allocs/op
62 | BenchmarkNext-4 100000 14200 ns/op 4660 B/op 117 allocs/op
63 | BenchmarkNextFiltered-4 100000 15284 ns/op 4859 B/op 123 allocs/op
64 | BenchmarkNextAll-4 20000 60889 ns/op 22774 B/op 157 allocs/op
65 | BenchmarkNextAllFiltered-4 20000 65125 ns/op 23869 B/op 166 allocs/op
66 | BenchmarkPrev-4 100000 14448 ns/op 4659 B/op 117 allocs/op
67 | BenchmarkPrevFiltered-4 100000 15444 ns/op 4859 B/op 123 allocs/op
68 | BenchmarkPrevAll-4 100000 22019 ns/op 7344 B/op 120 allocs/op
69 | BenchmarkPrevAllFiltered-4 100000 23307 ns/op 7545 B/op 126 allocs/op
70 | BenchmarkNextUntil-4 50000 30287 ns/op 8766 B/op 267 allocs/op
71 | BenchmarkNextUntilSelection-4 30000 41476 ns/op 19862 B/op 546 allocs/op
72 | BenchmarkNextUntilNodes-4 100000 16106 ns/op 8133 B/op 249 allocs/op
73 | BenchmarkPrevUntil-4 20000 98951 ns/op 25728 B/op 467 allocs/op
74 | BenchmarkPrevUntilSelection-4 30000 52390 ns/op 24875 B/op 694 allocs/op
75 | BenchmarkPrevUntilNodes-4 100000 12986 ns/op 6334 B/op 204 allocs/op
76 | BenchmarkNextFilteredUntil-4 100000 19365 ns/op 5908 B/op 177 allocs/op
77 | BenchmarkNextFilteredUntilSelection-4 30000 45334 ns/op 20555 B/op 571 allocs/op
78 | BenchmarkNextFilteredUntilNodes-4 30000 45292 ns/op 20556 B/op 571 allocs/op
79 | BenchmarkPrevFilteredUntil-4 100000 19412 ns/op 6032 B/op 179 allocs/op
80 | BenchmarkPrevFilteredUntilSelection-4 30000 46286 ns/op 21304 B/op 591 allocs/op
81 | BenchmarkPrevFilteredUntilNodes-4 30000 46554 ns/op 21305 B/op 591 allocs/op
82 | BenchmarkClosest-4 500000 3480 ns/op 160 B/op 8 allocs/op
83 | BenchmarkClosestSelection-4 2000000 722 ns/op 96 B/op 6 allocs/op
84 | BenchmarkClosestNodes-4 2000000 719 ns/op 96 B/op 6 allocs/op
85 | PASS
86 | ok github.com/PuerkitoBio/goquery 160.565s
87 |
--------------------------------------------------------------------------------
/bench_array_test.go:
--------------------------------------------------------------------------------
1 | package goquery
2 |
3 | import (
4 | "testing"
5 | )
6 |
7 | func BenchmarkFirst(b *testing.B) {
8 | b.StopTimer()
9 | sel := DocB().Find("dd")
10 | b.StartTimer()
11 | for i := 0; i < b.N; i++ {
12 | sel.First()
13 | }
14 | }
15 |
16 | func BenchmarkLast(b *testing.B) {
17 | b.StopTimer()
18 | sel := DocB().Find("dd")
19 | b.StartTimer()
20 | for i := 0; i < b.N; i++ {
21 | sel.Last()
22 | }
23 | }
24 |
25 | func BenchmarkEq(b *testing.B) {
26 | b.StopTimer()
27 | sel := DocB().Find("dd")
28 | j := 0
29 | b.StartTimer()
30 | for i := 0; i < b.N; i++ {
31 | sel.Eq(j)
32 | if j++; j >= sel.Length() {
33 | j = 0
34 | }
35 | }
36 | }
37 |
38 | func BenchmarkSlice(b *testing.B) {
39 | b.StopTimer()
40 | sel := DocB().Find("dd")
41 | j := 0
42 | b.StartTimer()
43 | for i := 0; i < b.N; i++ {
44 | sel.Slice(j, j+4)
45 | if j++; j >= (sel.Length() - 4) {
46 | j = 0
47 | }
48 | }
49 | }
50 |
51 | func BenchmarkGet(b *testing.B) {
52 | b.StopTimer()
53 | sel := DocB().Find("dd")
54 | j := 0
55 | b.StartTimer()
56 | for i := 0; i < b.N; i++ {
57 | sel.Get(j)
58 | if j++; j >= sel.Length() {
59 | j = 0
60 | }
61 | }
62 | }
63 |
64 | func BenchmarkIndex(b *testing.B) {
65 | var j int
66 |
67 | b.StopTimer()
68 | sel := DocB().Find("#Main")
69 | b.StartTimer()
70 | for i := 0; i < b.N; i++ {
71 | j = sel.Index()
72 | }
73 | if j != 3 {
74 | b.Fatalf("want 3, got %d", j)
75 | }
76 | }
77 |
78 | func BenchmarkIndexSelector(b *testing.B) {
79 | var j int
80 |
81 | b.StopTimer()
82 | sel := DocB().Find("#manual-nav dl dd:nth-child(1)")
83 | b.StartTimer()
84 | for i := 0; i < b.N; i++ {
85 | j = sel.IndexSelector("dd")
86 | }
87 | if j != 4 {
88 | b.Fatalf("want 4, got %d", j)
89 | }
90 | }
91 |
92 | func BenchmarkIndexOfNode(b *testing.B) {
93 | var j int
94 |
95 | b.StopTimer()
96 | sel := DocB().Find("span a")
97 | sel2 := DocB().Find("span a:nth-child(3)")
98 | n := sel2.Get(0)
99 | b.StartTimer()
100 | for i := 0; i < b.N; i++ {
101 | j = sel.IndexOfNode(n)
102 | }
103 | if j != 2 {
104 | b.Fatalf("want 2, got %d", j)
105 | }
106 | }
107 |
108 | func BenchmarkIndexOfSelection(b *testing.B) {
109 | var j int
110 | b.StopTimer()
111 | sel := DocB().Find("span a")
112 | sel2 := DocB().Find("span a:nth-child(3)")
113 | b.StartTimer()
114 | for i := 0; i < b.N; i++ {
115 | j = sel.IndexOfSelection(sel2)
116 | }
117 | if j != 2 {
118 | b.Fatalf("want 2, got %d", j)
119 | }
120 | }
121 |
--------------------------------------------------------------------------------
/bench_example_test.go:
--------------------------------------------------------------------------------
1 | package goquery
2 |
3 | import (
4 | "fmt"
5 | "strconv"
6 | "strings"
7 | "testing"
8 | )
9 |
10 | func BenchmarkMetalReviewExample(b *testing.B) {
11 | var n int
12 | var builder strings.Builder
13 |
14 | b.StopTimer()
15 | doc := loadDoc("metalreview.html")
16 | b.StartTimer()
17 | for i := 0; i < b.N; i++ {
18 | doc.Find(".slider-row:nth-child(1) .slider-item").Each(func(i int, s *Selection) {
19 | var band, title string
20 | var score float64
21 | var e error
22 |
23 | n++
24 | // For each item found, get the band, title and score, and print it
25 | band = s.Find("strong").Text()
26 | title = s.Find("em").Text()
27 | if score, e = strconv.ParseFloat(s.Find(".score").Text(), 64); e != nil {
28 | // Not a valid float, ignore score
29 | if n <= 4 {
30 | builder.WriteString(fmt.Sprintf("Review %d: %s - %s.\n", i, band, title))
31 | }
32 | } else {
33 | // Print all, including score
34 | if n <= 4 {
35 | builder.WriteString(fmt.Sprintf("Review %d: %s - %s (%2.1f).\n", i, band, title, score))
36 | }
37 | }
38 | })
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/bench_expand_test.go:
--------------------------------------------------------------------------------
1 | package goquery
2 |
3 | import (
4 | "testing"
5 | )
6 |
7 | func BenchmarkAdd(b *testing.B) {
8 | var n int
9 |
10 | b.StopTimer()
11 | sel := DocB().Find("dd")
12 | b.StartTimer()
13 | for i := 0; i < b.N; i++ {
14 | if n == 0 {
15 | n = sel.Add("h2[title]").Length()
16 | } else {
17 | sel.Add("h2[title]")
18 | }
19 | }
20 | if n != 43 {
21 | b.Fatalf("want 43, got %d", n)
22 | }
23 | }
24 |
25 | func BenchmarkAddSelection(b *testing.B) {
26 | var n int
27 |
28 | b.StopTimer()
29 | sel := DocB().Find("dd")
30 | sel2 := DocB().Find("h2[title]")
31 | b.StartTimer()
32 | for i := 0; i < b.N; i++ {
33 | if n == 0 {
34 | n = sel.AddSelection(sel2).Length()
35 | } else {
36 | sel.AddSelection(sel2)
37 | }
38 | }
39 | if n != 43 {
40 | b.Fatalf("want 43, got %d", n)
41 | }
42 | }
43 |
44 | func BenchmarkAddNodes(b *testing.B) {
45 | var n int
46 |
47 | b.StopTimer()
48 | sel := DocB().Find("dd")
49 | sel2 := DocB().Find("h2[title]")
50 | nodes := sel2.Nodes
51 | b.StartTimer()
52 | for i := 0; i < b.N; i++ {
53 | if n == 0 {
54 | n = sel.AddNodes(nodes...).Length()
55 | } else {
56 | sel.AddNodes(nodes...)
57 | }
58 | }
59 | if n != 43 {
60 | b.Fatalf("want 43, got %d", n)
61 | }
62 | }
63 |
64 | func BenchmarkAddNodesBig(b *testing.B) {
65 | var n int
66 |
67 | doc := DocW()
68 | sel := doc.Find("li")
69 | // make nodes > 1000
70 | nodes := sel.Nodes
71 | nodes = append(nodes, nodes...)
72 | nodes = append(nodes, nodes...)
73 | sel = doc.Find("xyz")
74 | b.ResetTimer()
75 |
76 | for i := 0; i < b.N; i++ {
77 | if n == 0 {
78 | n = sel.AddNodes(nodes...).Length()
79 | } else {
80 | sel.AddNodes(nodes...)
81 | }
82 | }
83 | if n != 373 {
84 | b.Fatalf("want 373, got %d", n)
85 | }
86 | }
87 |
88 | func BenchmarkAndSelf(b *testing.B) {
89 | var n int
90 |
91 | b.StopTimer()
92 | sel := DocB().Find("dd").Parent()
93 | b.StartTimer()
94 | for i := 0; i < b.N; i++ {
95 | if n == 0 {
96 | n = sel.AndSelf().Length()
97 | } else {
98 | sel.AndSelf()
99 | }
100 | }
101 | if n != 44 {
102 | b.Fatalf("want 44, got %d", n)
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/bench_filter_test.go:
--------------------------------------------------------------------------------
1 | package goquery
2 |
3 | import (
4 | "testing"
5 | )
6 |
7 | func BenchmarkFilter(b *testing.B) {
8 | var n int
9 |
10 | b.StopTimer()
11 | sel := DocW().Find("li")
12 | b.StartTimer()
13 | for i := 0; i < b.N; i++ {
14 | if n == 0 {
15 | n = sel.Filter(".toclevel-1").Length()
16 | } else {
17 | sel.Filter(".toclevel-1")
18 | }
19 | }
20 | if n != 13 {
21 | b.Fatalf("want 13, got %d", n)
22 | }
23 | }
24 |
25 | func BenchmarkNot(b *testing.B) {
26 | var n int
27 |
28 | b.StopTimer()
29 | sel := DocW().Find("li")
30 | b.StartTimer()
31 | for i := 0; i < b.N; i++ {
32 | if n == 0 {
33 | n = sel.Not(".toclevel-2").Length()
34 | } else {
35 | sel.Filter(".toclevel-2")
36 | }
37 | }
38 | if n != 371 {
39 | b.Fatalf("want 371, got %d", n)
40 | }
41 | }
42 |
43 | func BenchmarkFilterFunction(b *testing.B) {
44 | var n int
45 |
46 | b.StopTimer()
47 | sel := DocW().Find("li")
48 | f := func(i int, s *Selection) bool {
49 | return len(s.Get(0).Attr) > 0
50 | }
51 | b.StartTimer()
52 | for i := 0; i < b.N; i++ {
53 | if n == 0 {
54 | n = sel.FilterFunction(f).Length()
55 | } else {
56 | sel.FilterFunction(f)
57 | }
58 | }
59 | if n != 112 {
60 | b.Fatalf("want 112, got %d", n)
61 | }
62 | }
63 |
64 | func BenchmarkNotFunction(b *testing.B) {
65 | var n int
66 |
67 | b.StopTimer()
68 | sel := DocW().Find("li")
69 | f := func(i int, s *Selection) bool {
70 | return len(s.Get(0).Attr) > 0
71 | }
72 | b.StartTimer()
73 | for i := 0; i < b.N; i++ {
74 | if n == 0 {
75 | n = sel.NotFunction(f).Length()
76 | } else {
77 | sel.NotFunction(f)
78 | }
79 | }
80 | if n != 261 {
81 | b.Fatalf("want 261, got %d", n)
82 | }
83 | }
84 |
85 | func BenchmarkFilterNodes(b *testing.B) {
86 | var n int
87 |
88 | b.StopTimer()
89 | sel := DocW().Find("li")
90 | sel2 := DocW().Find(".toclevel-2")
91 | nodes := sel2.Nodes
92 | b.StartTimer()
93 | for i := 0; i < b.N; i++ {
94 | if n == 0 {
95 | n = sel.FilterNodes(nodes...).Length()
96 | } else {
97 | sel.FilterNodes(nodes...)
98 | }
99 | }
100 | if n != 2 {
101 | b.Fatalf("want 2, got %d", n)
102 | }
103 | }
104 |
105 | func BenchmarkNotNodes(b *testing.B) {
106 | var n int
107 |
108 | b.StopTimer()
109 | sel := DocW().Find("li")
110 | sel2 := DocW().Find(".toclevel-1")
111 | nodes := sel2.Nodes
112 | b.StartTimer()
113 | for i := 0; i < b.N; i++ {
114 | if n == 0 {
115 | n = sel.NotNodes(nodes...).Length()
116 | } else {
117 | sel.NotNodes(nodes...)
118 | }
119 | }
120 | if n != 360 {
121 | b.Fatalf("want 360, got %d", n)
122 | }
123 | }
124 |
125 | func BenchmarkFilterSelection(b *testing.B) {
126 | var n int
127 |
128 | b.StopTimer()
129 | sel := DocW().Find("li")
130 | sel2 := DocW().Find(".toclevel-2")
131 | b.StartTimer()
132 | for i := 0; i < b.N; i++ {
133 | if n == 0 {
134 | n = sel.FilterSelection(sel2).Length()
135 | } else {
136 | sel.FilterSelection(sel2)
137 | }
138 | }
139 | if n != 2 {
140 | b.Fatalf("want 2, got %d", n)
141 | }
142 | }
143 |
144 | func BenchmarkNotSelection(b *testing.B) {
145 | var n int
146 |
147 | b.StopTimer()
148 | sel := DocW().Find("li")
149 | sel2 := DocW().Find(".toclevel-1")
150 | b.StartTimer()
151 | for i := 0; i < b.N; i++ {
152 | if n == 0 {
153 | n = sel.NotSelection(sel2).Length()
154 | } else {
155 | sel.NotSelection(sel2)
156 | }
157 | }
158 | if n != 360 {
159 | b.Fatalf("want 360, got %d", n)
160 | }
161 | }
162 |
163 | func BenchmarkHas(b *testing.B) {
164 | var n int
165 |
166 | b.StopTimer()
167 | sel := DocW().Find("h2")
168 | b.StartTimer()
169 | for i := 0; i < b.N; i++ {
170 | if n == 0 {
171 | n = sel.Has(".editsection").Length()
172 | } else {
173 | sel.Has(".editsection")
174 | }
175 | }
176 | if n != 13 {
177 | b.Fatalf("want 13, got %d", n)
178 | }
179 | }
180 |
181 | func BenchmarkHasNodes(b *testing.B) {
182 | var n int
183 |
184 | b.StopTimer()
185 | sel := DocW().Find("li")
186 | sel2 := DocW().Find(".tocnumber")
187 | nodes := sel2.Nodes
188 | b.StartTimer()
189 | for i := 0; i < b.N; i++ {
190 | if n == 0 {
191 | n = sel.HasNodes(nodes...).Length()
192 | } else {
193 | sel.HasNodes(nodes...)
194 | }
195 | }
196 | if n != 15 {
197 | b.Fatalf("want 15, got %d", n)
198 | }
199 | }
200 |
201 | func BenchmarkHasSelection(b *testing.B) {
202 | var n int
203 |
204 | b.StopTimer()
205 | sel := DocW().Find("li")
206 | sel2 := DocW().Find(".tocnumber")
207 | b.StartTimer()
208 | for i := 0; i < b.N; i++ {
209 | if n == 0 {
210 | n = sel.HasSelection(sel2).Length()
211 | } else {
212 | sel.HasSelection(sel2)
213 | }
214 | }
215 | if n != 15 {
216 | b.Fatalf("want 15, got %d", n)
217 | }
218 | }
219 |
220 | func BenchmarkEnd(b *testing.B) {
221 | var n int
222 |
223 | b.StopTimer()
224 | sel := DocW().Find("li").Has(".tocnumber")
225 | b.StartTimer()
226 | for i := 0; i < b.N; i++ {
227 | if n == 0 {
228 | n = sel.End().Length()
229 | } else {
230 | sel.End()
231 | }
232 | }
233 | if n != 373 {
234 | b.Fatalf("want 373, got %d", n)
235 | }
236 | }
237 |
--------------------------------------------------------------------------------
/bench_iteration_test.go:
--------------------------------------------------------------------------------
1 | package goquery
2 |
3 | import (
4 | "strconv"
5 | "testing"
6 | )
7 |
8 | func BenchmarkEach(b *testing.B) {
9 | var tmp, n int
10 |
11 | b.StopTimer()
12 | sel := DocW().Find("td")
13 | f := func(i int, s *Selection) {
14 | tmp++
15 | }
16 | b.StartTimer()
17 | for i := 0; i < b.N; i++ {
18 | sel.Each(f)
19 | if n == 0 {
20 | n = tmp
21 | }
22 | }
23 | if n != 59 {
24 | b.Fatalf("want 59, got %d", n)
25 | }
26 | }
27 |
28 | func BenchmarkEachIter(b *testing.B) {
29 | var tmp, n int
30 |
31 | b.StopTimer()
32 | sel := DocW().Find("td")
33 | b.StartTimer()
34 | for i := 0; i < b.N; i++ {
35 | for range sel.EachIter() {
36 | tmp++
37 | }
38 | if n == 0 {
39 | n = tmp
40 | }
41 | }
42 | if n != 59 {
43 | b.Fatalf("want 59, got %d", n)
44 | }
45 | }
46 |
47 | func BenchmarkEachIterWithBreak(b *testing.B) {
48 | var tmp, n int
49 |
50 | b.StopTimer()
51 | sel := DocW().Find("td")
52 | b.StartTimer()
53 | for i := 0; i < b.N; i++ {
54 | tmp = 0
55 | for range sel.EachIter() {
56 | tmp++
57 | if tmp >= 10 {
58 | break
59 | }
60 | }
61 | if n == 0 {
62 | n = tmp
63 | }
64 | }
65 | if n != 10 {
66 | b.Fatalf("want 10, got %d", n)
67 | }
68 | }
69 |
70 | func BenchmarkMap(b *testing.B) {
71 | var tmp, n int
72 |
73 | b.StopTimer()
74 | sel := DocW().Find("td")
75 | f := func(i int, s *Selection) string {
76 | tmp++
77 | return strconv.Itoa(tmp)
78 | }
79 | b.StartTimer()
80 | for i := 0; i < b.N; i++ {
81 | sel.Map(f)
82 | if n == 0 {
83 | n = tmp
84 | }
85 | }
86 | if n != 59 {
87 | b.Fatalf("want 59, got %d", n)
88 | }
89 | }
90 |
91 | func BenchmarkEachWithBreak(b *testing.B) {
92 | var tmp, n int
93 |
94 | b.StopTimer()
95 | sel := DocW().Find("td")
96 | f := func(i int, s *Selection) bool {
97 | tmp++
98 | return tmp < 10
99 | }
100 | b.StartTimer()
101 | for i := 0; i < b.N; i++ {
102 | tmp = 0
103 | sel.EachWithBreak(f)
104 | if n == 0 {
105 | n = tmp
106 | }
107 | }
108 | if n != 10 {
109 | b.Fatalf("want 10, got %d", n)
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/bench_property_test.go:
--------------------------------------------------------------------------------
1 | package goquery
2 |
3 | import (
4 | "testing"
5 | )
6 |
7 | func BenchmarkAttr(b *testing.B) {
8 | var s string
9 |
10 | b.StopTimer()
11 | sel := DocW().Find("h1")
12 | b.StartTimer()
13 | for i := 0; i < b.N; i++ {
14 | s, _ = sel.Attr("id")
15 | }
16 | if s != "firstHeading" {
17 | b.Fatalf("want firstHeading, got %q", s)
18 | }
19 | }
20 |
21 | func BenchmarkText(b *testing.B) {
22 | b.StopTimer()
23 | sel := DocW().Find("h2")
24 | b.StartTimer()
25 | for i := 0; i < b.N; i++ {
26 | sel.Text()
27 | }
28 | }
29 |
30 | func BenchmarkLength(b *testing.B) {
31 | var n int
32 |
33 | b.StopTimer()
34 | sel := DocW().Find("h2")
35 | b.StartTimer()
36 | for i := 0; i < b.N; i++ {
37 | n = sel.Length()
38 | }
39 | if n != 14 {
40 | b.Fatalf("want 14, got %d", n)
41 | }
42 | }
43 |
44 | func BenchmarkHtml(b *testing.B) {
45 | b.StopTimer()
46 | sel := DocW().Find("h2")
47 | b.StartTimer()
48 | for i := 0; i < b.N; i++ {
49 | _, _ = sel.Html()
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/bench_query_test.go:
--------------------------------------------------------------------------------
1 | package goquery
2 |
3 | import (
4 | "testing"
5 | )
6 |
7 | func BenchmarkIs(b *testing.B) {
8 | var y bool
9 |
10 | b.StopTimer()
11 | sel := DocW().Find("li")
12 | b.StartTimer()
13 | for i := 0; i < b.N; i++ {
14 | y = sel.Is(".toclevel-2")
15 | }
16 | if !y {
17 | b.Fatal("want true")
18 | }
19 | }
20 |
21 | func BenchmarkIsPositional(b *testing.B) {
22 | var y bool
23 |
24 | b.StopTimer()
25 | sel := DocW().Find("li")
26 | b.StartTimer()
27 | for i := 0; i < b.N; i++ {
28 | y = sel.Is("li:nth-child(2)")
29 | }
30 | if !y {
31 | b.Fatal("want true")
32 | }
33 | }
34 |
35 | func BenchmarkIsFunction(b *testing.B) {
36 | var y bool
37 |
38 | b.StopTimer()
39 | sel := DocW().Find(".toclevel-1")
40 | f := func(i int, s *Selection) bool {
41 | return i == 8
42 | }
43 | b.StartTimer()
44 | for i := 0; i < b.N; i++ {
45 | y = sel.IsFunction(f)
46 | }
47 | if !y {
48 | b.Fatal("want true")
49 | }
50 | }
51 |
52 | func BenchmarkIsSelection(b *testing.B) {
53 | var y bool
54 |
55 | b.StopTimer()
56 | sel := DocW().Find("li")
57 | sel2 := DocW().Find(".toclevel-2")
58 | b.StartTimer()
59 | for i := 0; i < b.N; i++ {
60 | y = sel.IsSelection(sel2)
61 | }
62 | if !y {
63 | b.Fatal("want true")
64 | }
65 | }
66 |
67 | func BenchmarkIsNodes(b *testing.B) {
68 | var y bool
69 |
70 | b.StopTimer()
71 | sel := DocW().Find("li")
72 | sel2 := DocW().Find(".toclevel-2")
73 | nodes := sel2.Nodes
74 | b.StartTimer()
75 | for i := 0; i < b.N; i++ {
76 | y = sel.IsNodes(nodes...)
77 | }
78 | if !y {
79 | b.Fatal("want true")
80 | }
81 | }
82 |
83 | func BenchmarkHasClass(b *testing.B) {
84 | var y bool
85 |
86 | b.StopTimer()
87 | sel := DocW().Find("span")
88 | b.StartTimer()
89 | for i := 0; i < b.N; i++ {
90 | y = sel.HasClass("official")
91 | }
92 | if !y {
93 | b.Fatal("want true")
94 | }
95 | }
96 |
97 | func BenchmarkContains(b *testing.B) {
98 | var y bool
99 |
100 | b.StopTimer()
101 | sel := DocW().Find("span.url")
102 | sel2 := DocW().Find("a[rel=\"nofollow\"]")
103 | node := sel2.Nodes[0]
104 | b.StartTimer()
105 | for i := 0; i < b.N; i++ {
106 | y = sel.Contains(node)
107 | }
108 | if !y {
109 | b.Fatal("want true")
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/bench_traversal_test.go:
--------------------------------------------------------------------------------
1 | package goquery
2 |
3 | import (
4 | "testing"
5 |
6 | "github.com/andybalholm/cascadia"
7 | )
8 |
9 | func BenchmarkFind(b *testing.B) {
10 | var n int
11 |
12 | for i := 0; i < b.N; i++ {
13 | if n == 0 {
14 | n = DocB().Find("dd").Length()
15 |
16 | } else {
17 | DocB().Find("dd")
18 | }
19 | }
20 | if n != 41 {
21 | b.Fatalf("want 41, got %d", n)
22 | }
23 | }
24 |
25 | func BenchmarkFindWithinSelection(b *testing.B) {
26 | var n int
27 |
28 | b.StopTimer()
29 | sel := DocW().Find("ul")
30 | b.StartTimer()
31 | for i := 0; i < b.N; i++ {
32 | if n == 0 {
33 | n = sel.Find("a[class]").Length()
34 | } else {
35 | sel.Find("a[class]")
36 | }
37 | }
38 | if n != 39 {
39 | b.Fatalf("want 39, got %d", n)
40 | }
41 | }
42 |
43 | func BenchmarkFindSelection(b *testing.B) {
44 | var n int
45 |
46 | b.StopTimer()
47 | sel := DocW().Find("ul")
48 | sel2 := DocW().Find("span")
49 | b.StartTimer()
50 | for i := 0; i < b.N; i++ {
51 | if n == 0 {
52 | n = sel.FindSelection(sel2).Length()
53 | } else {
54 | sel.FindSelection(sel2)
55 | }
56 | }
57 | if n != 73 {
58 | b.Fatalf("want 73, got %d", n)
59 | }
60 | }
61 |
62 | func BenchmarkFindNodes(b *testing.B) {
63 | var n int
64 |
65 | b.StopTimer()
66 | sel := DocW().Find("ul")
67 | sel2 := DocW().Find("span")
68 | nodes := sel2.Nodes
69 | b.StartTimer()
70 | for i := 0; i < b.N; i++ {
71 | if n == 0 {
72 | n = sel.FindNodes(nodes...).Length()
73 | } else {
74 | sel.FindNodes(nodes...)
75 | }
76 | }
77 | if n != 73 {
78 | b.Fatalf("want 73, got %d", n)
79 | }
80 | }
81 |
82 | func BenchmarkContents(b *testing.B) {
83 | var n int
84 |
85 | b.StopTimer()
86 | sel := DocW().Find(".toclevel-1")
87 | b.StartTimer()
88 | for i := 0; i < b.N; i++ {
89 | if n == 0 {
90 | n = sel.Contents().Length()
91 | } else {
92 | sel.Contents()
93 | }
94 | }
95 | if n != 16 {
96 | b.Fatalf("want 16, got %d", n)
97 | }
98 | }
99 |
100 | func BenchmarkContentsFiltered(b *testing.B) {
101 | var n int
102 |
103 | b.StopTimer()
104 | sel := DocW().Find(".toclevel-1")
105 | b.StartTimer()
106 | for i := 0; i < b.N; i++ {
107 | if n == 0 {
108 | n = sel.ContentsFiltered("a[href=\"#Examples\"]").Length()
109 | } else {
110 | sel.ContentsFiltered("a[href=\"#Examples\"]")
111 | }
112 | }
113 | if n != 1 {
114 | b.Fatalf("want 1, got %d", n)
115 | }
116 | }
117 |
118 | func BenchmarkChildren(b *testing.B) {
119 | var n int
120 |
121 | b.StopTimer()
122 | sel := DocW().Find(".toclevel-2")
123 | b.StartTimer()
124 | for i := 0; i < b.N; i++ {
125 | if n == 0 {
126 | n = sel.Children().Length()
127 | } else {
128 | sel.Children()
129 | }
130 | }
131 | if n != 2 {
132 | b.Fatalf("want 2, got %d", n)
133 | }
134 | }
135 |
136 | func BenchmarkChildrenFiltered(b *testing.B) {
137 | var n int
138 |
139 | b.StopTimer()
140 | sel := DocW().Find("h3")
141 | b.StartTimer()
142 | for i := 0; i < b.N; i++ {
143 | if n == 0 {
144 | n = sel.ChildrenFiltered(".editsection").Length()
145 | } else {
146 | sel.ChildrenFiltered(".editsection")
147 | }
148 | }
149 | if n != 2 {
150 | b.Fatalf("want 2, got %d", n)
151 | }
152 | }
153 |
154 | func BenchmarkParent(b *testing.B) {
155 | var n int
156 |
157 | b.StopTimer()
158 | sel := DocW().Find("li")
159 | b.StartTimer()
160 | for i := 0; i < b.N; i++ {
161 | if n == 0 {
162 | n = sel.Parent().Length()
163 | } else {
164 | sel.Parent()
165 | }
166 | }
167 | if n != 55 {
168 | b.Fatalf("want 55, got %d", n)
169 | }
170 | }
171 |
172 | func BenchmarkParentFiltered(b *testing.B) {
173 | var n int
174 |
175 | b.StopTimer()
176 | sel := DocW().Find("li")
177 | b.StartTimer()
178 | for i := 0; i < b.N; i++ {
179 | if n == 0 {
180 | n = sel.ParentFiltered("ul[id]").Length()
181 | } else {
182 | sel.ParentFiltered("ul[id]")
183 | }
184 | }
185 | if n != 4 {
186 | b.Fatalf("want 4, got %d", n)
187 | }
188 | }
189 |
190 | func BenchmarkParents(b *testing.B) {
191 | var n int
192 |
193 | b.StopTimer()
194 | sel := DocW().Find("th a")
195 | b.StartTimer()
196 | for i := 0; i < b.N; i++ {
197 | if n == 0 {
198 | n = sel.Parents().Length()
199 | } else {
200 | sel.Parents()
201 | }
202 | }
203 | if n != 73 {
204 | b.Fatalf("want 73, got %d", n)
205 | }
206 | }
207 |
208 | func BenchmarkParentsFiltered(b *testing.B) {
209 | var n int
210 |
211 | b.StopTimer()
212 | sel := DocW().Find("th a")
213 | b.StartTimer()
214 | for i := 0; i < b.N; i++ {
215 | if n == 0 {
216 | n = sel.ParentsFiltered("tr").Length()
217 | } else {
218 | sel.ParentsFiltered("tr")
219 | }
220 | }
221 | if n != 18 {
222 | b.Fatalf("want 18, got %d", n)
223 | }
224 | }
225 |
226 | func BenchmarkParentsUntil(b *testing.B) {
227 | var n int
228 |
229 | b.StopTimer()
230 | sel := DocW().Find("th a")
231 | b.StartTimer()
232 | for i := 0; i < b.N; i++ {
233 | if n == 0 {
234 | n = sel.ParentsUntil("table").Length()
235 | } else {
236 | sel.ParentsUntil("table")
237 | }
238 | }
239 | if n != 52 {
240 | b.Fatalf("want 52, got %d", n)
241 | }
242 | }
243 |
244 | func BenchmarkParentsUntilSelection(b *testing.B) {
245 | var n int
246 |
247 | b.StopTimer()
248 | sel := DocW().Find("th a")
249 | sel2 := DocW().Find("#content")
250 | b.StartTimer()
251 | for i := 0; i < b.N; i++ {
252 | if n == 0 {
253 | n = sel.ParentsUntilSelection(sel2).Length()
254 | } else {
255 | sel.ParentsUntilSelection(sel2)
256 | }
257 | }
258 | if n != 70 {
259 | b.Fatalf("want 70, got %d", n)
260 | }
261 | }
262 |
263 | func BenchmarkParentsUntilNodes(b *testing.B) {
264 | var n int
265 |
266 | b.StopTimer()
267 | sel := DocW().Find("th a")
268 | sel2 := DocW().Find("#content")
269 | nodes := sel2.Nodes
270 | b.StartTimer()
271 | for i := 0; i < b.N; i++ {
272 | if n == 0 {
273 | n = sel.ParentsUntilNodes(nodes...).Length()
274 | } else {
275 | sel.ParentsUntilNodes(nodes...)
276 | }
277 | }
278 | if n != 70 {
279 | b.Fatalf("want 70, got %d", n)
280 | }
281 | }
282 |
283 | func BenchmarkParentsFilteredUntil(b *testing.B) {
284 | var n int
285 |
286 | b.StopTimer()
287 | sel := DocW().Find(".toclevel-1 a")
288 | b.StartTimer()
289 | for i := 0; i < b.N; i++ {
290 | if n == 0 {
291 | n = sel.ParentsFilteredUntil(":nth-child(1)", "ul").Length()
292 | } else {
293 | sel.ParentsFilteredUntil(":nth-child(1)", "ul")
294 | }
295 | }
296 | if n != 2 {
297 | b.Fatalf("want 2, got %d", n)
298 | }
299 | }
300 |
301 | func BenchmarkParentsFilteredUntilSelection(b *testing.B) {
302 | var n int
303 |
304 | b.StopTimer()
305 | sel := DocW().Find(".toclevel-1 a")
306 | sel2 := DocW().Find("ul")
307 | b.StartTimer()
308 | for i := 0; i < b.N; i++ {
309 | if n == 0 {
310 | n = sel.ParentsFilteredUntilSelection(":nth-child(1)", sel2).Length()
311 | } else {
312 | sel.ParentsFilteredUntilSelection(":nth-child(1)", sel2)
313 | }
314 | }
315 | if n != 2 {
316 | b.Fatalf("want 2, got %d", n)
317 | }
318 | }
319 |
320 | func BenchmarkParentsFilteredUntilNodes(b *testing.B) {
321 | var n int
322 |
323 | b.StopTimer()
324 | sel := DocW().Find(".toclevel-1 a")
325 | sel2 := DocW().Find("ul")
326 | nodes := sel2.Nodes
327 | b.StartTimer()
328 | for i := 0; i < b.N; i++ {
329 | if n == 0 {
330 | n = sel.ParentsFilteredUntilNodes(":nth-child(1)", nodes...).Length()
331 | } else {
332 | sel.ParentsFilteredUntilNodes(":nth-child(1)", nodes...)
333 | }
334 | }
335 | if n != 2 {
336 | b.Fatalf("want 2, got %d", n)
337 | }
338 | }
339 |
340 | func BenchmarkSiblings(b *testing.B) {
341 | var n int
342 |
343 | b.StopTimer()
344 | sel := DocW().Find("ul li:nth-child(1)")
345 | b.StartTimer()
346 | for i := 0; i < b.N; i++ {
347 | if n == 0 {
348 | n = sel.Siblings().Length()
349 | } else {
350 | sel.Siblings()
351 | }
352 | }
353 | if n != 293 {
354 | b.Fatalf("want 293, got %d", n)
355 | }
356 | }
357 |
358 | func BenchmarkSiblingsFiltered(b *testing.B) {
359 | var n int
360 |
361 | b.StopTimer()
362 | sel := DocW().Find("ul li:nth-child(1)")
363 | b.StartTimer()
364 | for i := 0; i < b.N; i++ {
365 | if n == 0 {
366 | n = sel.SiblingsFiltered("[class]").Length()
367 | } else {
368 | sel.SiblingsFiltered("[class]")
369 | }
370 | }
371 | if n != 46 {
372 | b.Fatalf("want 46, got %d", n)
373 | }
374 | }
375 |
376 | func BenchmarkNext(b *testing.B) {
377 | var n int
378 |
379 | b.StopTimer()
380 | sel := DocW().Find("li:nth-child(1)")
381 | b.StartTimer()
382 | for i := 0; i < b.N; i++ {
383 | if n == 0 {
384 | n = sel.Next().Length()
385 | } else {
386 | sel.Next()
387 | }
388 | }
389 | if n != 49 {
390 | b.Fatalf("want 49, got %d", n)
391 | }
392 | }
393 |
394 | func BenchmarkNextFiltered(b *testing.B) {
395 | var n int
396 |
397 | b.StopTimer()
398 | sel := DocW().Find("li:nth-child(1)")
399 | b.StartTimer()
400 | for i := 0; i < b.N; i++ {
401 | if n == 0 {
402 | n = sel.NextFiltered("[class]").Length()
403 | } else {
404 | sel.NextFiltered("[class]")
405 | }
406 | }
407 | if n != 6 {
408 | b.Fatalf("want 6, got %d", n)
409 | }
410 | }
411 |
412 | func BenchmarkNextAll(b *testing.B) {
413 | var n int
414 |
415 | b.StopTimer()
416 | sel := DocW().Find("li:nth-child(3)")
417 | b.StartTimer()
418 | for i := 0; i < b.N; i++ {
419 | if n == 0 {
420 | n = sel.NextAll().Length()
421 | } else {
422 | sel.NextAll()
423 | }
424 | }
425 | if n != 234 {
426 | b.Fatalf("want 234, got %d", n)
427 | }
428 | }
429 |
430 | func BenchmarkNextAllFiltered(b *testing.B) {
431 | var n int
432 |
433 | b.StopTimer()
434 | sel := DocW().Find("li:nth-child(3)")
435 | b.StartTimer()
436 | for i := 0; i < b.N; i++ {
437 | if n == 0 {
438 | n = sel.NextAllFiltered("[class]").Length()
439 | } else {
440 | sel.NextAllFiltered("[class]")
441 | }
442 | }
443 | if n != 33 {
444 | b.Fatalf("want 33, got %d", n)
445 | }
446 | }
447 |
448 | func BenchmarkPrev(b *testing.B) {
449 | var n int
450 |
451 | b.StopTimer()
452 | sel := DocW().Find("li:last-child")
453 | b.StartTimer()
454 | for i := 0; i < b.N; i++ {
455 | if n == 0 {
456 | n = sel.Prev().Length()
457 | } else {
458 | sel.Prev()
459 | }
460 | }
461 | if n != 49 {
462 | b.Fatalf("want 49, got %d", n)
463 | }
464 | }
465 |
466 | func BenchmarkPrevFiltered(b *testing.B) {
467 | var n int
468 |
469 | b.StopTimer()
470 | sel := DocW().Find("li:last-child")
471 | b.StartTimer()
472 | for i := 0; i < b.N; i++ {
473 | if n == 0 {
474 | n = sel.PrevFiltered("[class]").Length()
475 | } else {
476 | sel.PrevFiltered("[class]")
477 | }
478 | }
479 | // There is one more Prev li with a class, compared to Next li with a class
480 | // (confirmed by looking at the HTML, this is ok)
481 | if n != 7 {
482 | b.Fatalf("want 7, got %d", n)
483 | }
484 | }
485 |
486 | func BenchmarkPrevAll(b *testing.B) {
487 | var n int
488 |
489 | b.StopTimer()
490 | sel := DocW().Find("li:nth-child(4)")
491 | b.StartTimer()
492 | for i := 0; i < b.N; i++ {
493 | if n == 0 {
494 | n = sel.PrevAll().Length()
495 | } else {
496 | sel.PrevAll()
497 | }
498 | }
499 | if n != 78 {
500 | b.Fatalf("want 78, got %d", n)
501 | }
502 | }
503 |
504 | func BenchmarkPrevAllFiltered(b *testing.B) {
505 | var n int
506 |
507 | b.StopTimer()
508 | sel := DocW().Find("li:nth-child(4)")
509 | b.StartTimer()
510 | for i := 0; i < b.N; i++ {
511 | if n == 0 {
512 | n = sel.PrevAllFiltered("[class]").Length()
513 | } else {
514 | sel.PrevAllFiltered("[class]")
515 | }
516 | }
517 | if n != 6 {
518 | b.Fatalf("want 6, got %d", n)
519 | }
520 | }
521 |
522 | func BenchmarkNextUntil(b *testing.B) {
523 | var n int
524 |
525 | b.StopTimer()
526 | sel := DocW().Find("li:first-child")
527 | b.StartTimer()
528 | for i := 0; i < b.N; i++ {
529 | if n == 0 {
530 | n = sel.NextUntil(":nth-child(4)").Length()
531 | } else {
532 | sel.NextUntil(":nth-child(4)")
533 | }
534 | }
535 | if n != 84 {
536 | b.Fatalf("want 84, got %d", n)
537 | }
538 | }
539 |
540 | func BenchmarkNextUntilSelection(b *testing.B) {
541 | var n int
542 |
543 | b.StopTimer()
544 | sel := DocW().Find("h2")
545 | sel2 := DocW().Find("ul")
546 | b.StartTimer()
547 | for i := 0; i < b.N; i++ {
548 | if n == 0 {
549 | n = sel.NextUntilSelection(sel2).Length()
550 | } else {
551 | sel.NextUntilSelection(sel2)
552 | }
553 | }
554 | if n != 42 {
555 | b.Fatalf("want 42, got %d", n)
556 | }
557 | }
558 |
559 | func BenchmarkNextUntilNodes(b *testing.B) {
560 | var n int
561 |
562 | b.StopTimer()
563 | sel := DocW().Find("h2")
564 | sel2 := DocW().Find("p")
565 | nodes := sel2.Nodes
566 | b.StartTimer()
567 | for i := 0; i < b.N; i++ {
568 | if n == 0 {
569 | n = sel.NextUntilNodes(nodes...).Length()
570 | } else {
571 | sel.NextUntilNodes(nodes...)
572 | }
573 | }
574 | if n != 12 {
575 | b.Fatalf("want 12, got %d", n)
576 | }
577 | }
578 |
579 | func BenchmarkPrevUntil(b *testing.B) {
580 | var n int
581 |
582 | b.StopTimer()
583 | sel := DocW().Find("li:last-child")
584 | b.StartTimer()
585 | for i := 0; i < b.N; i++ {
586 | if n == 0 {
587 | n = sel.PrevUntil(":nth-child(4)").Length()
588 | } else {
589 | sel.PrevUntil(":nth-child(4)")
590 | }
591 | }
592 | if n != 238 {
593 | b.Fatalf("want 238, got %d", n)
594 | }
595 | }
596 |
597 | func BenchmarkPrevUntilSelection(b *testing.B) {
598 | var n int
599 |
600 | b.StopTimer()
601 | sel := DocW().Find("h2")
602 | sel2 := DocW().Find("ul")
603 | b.StartTimer()
604 | for i := 0; i < b.N; i++ {
605 | if n == 0 {
606 | n = sel.PrevUntilSelection(sel2).Length()
607 | } else {
608 | sel.PrevUntilSelection(sel2)
609 | }
610 | }
611 | if n != 49 {
612 | b.Fatalf("want 49, got %d", n)
613 | }
614 | }
615 |
616 | func BenchmarkPrevUntilNodes(b *testing.B) {
617 | var n int
618 |
619 | b.StopTimer()
620 | sel := DocW().Find("h2")
621 | sel2 := DocW().Find("p")
622 | nodes := sel2.Nodes
623 | b.StartTimer()
624 | for i := 0; i < b.N; i++ {
625 | if n == 0 {
626 | n = sel.PrevUntilNodes(nodes...).Length()
627 | } else {
628 | sel.PrevUntilNodes(nodes...)
629 | }
630 | }
631 | if n != 11 {
632 | b.Fatalf("want 11, got %d", n)
633 | }
634 | }
635 |
636 | func BenchmarkNextFilteredUntil(b *testing.B) {
637 | var n int
638 |
639 | b.StopTimer()
640 | sel := DocW().Find("h2")
641 | b.StartTimer()
642 | for i := 0; i < b.N; i++ {
643 | if n == 0 {
644 | n = sel.NextFilteredUntil("p", "div").Length()
645 | } else {
646 | sel.NextFilteredUntil("p", "div")
647 | }
648 | }
649 | if n != 22 {
650 | b.Fatalf("want 22, got %d", n)
651 | }
652 | }
653 |
654 | func BenchmarkNextFilteredUntilSelection(b *testing.B) {
655 | var n int
656 |
657 | b.StopTimer()
658 | sel := DocW().Find("h2")
659 | sel2 := DocW().Find("div")
660 | b.StartTimer()
661 | for i := 0; i < b.N; i++ {
662 | if n == 0 {
663 | n = sel.NextFilteredUntilSelection("p", sel2).Length()
664 | } else {
665 | sel.NextFilteredUntilSelection("p", sel2)
666 | }
667 | }
668 | if n != 22 {
669 | b.Fatalf("want 22, got %d", n)
670 | }
671 | }
672 |
673 | func BenchmarkNextFilteredUntilNodes(b *testing.B) {
674 | var n int
675 |
676 | b.StopTimer()
677 | sel := DocW().Find("h2")
678 | sel2 := DocW().Find("div")
679 | nodes := sel2.Nodes
680 | b.StartTimer()
681 | for i := 0; i < b.N; i++ {
682 | if n == 0 {
683 | n = sel.NextFilteredUntilNodes("p", nodes...).Length()
684 | } else {
685 | sel.NextFilteredUntilNodes("p", nodes...)
686 | }
687 | }
688 | if n != 22 {
689 | b.Fatalf("want 22, got %d", n)
690 | }
691 | }
692 |
693 | func BenchmarkPrevFilteredUntil(b *testing.B) {
694 | var n int
695 |
696 | b.StopTimer()
697 | sel := DocW().Find("h2")
698 | b.StartTimer()
699 | for i := 0; i < b.N; i++ {
700 | if n == 0 {
701 | n = sel.PrevFilteredUntil("p", "div").Length()
702 | } else {
703 | sel.PrevFilteredUntil("p", "div")
704 | }
705 | }
706 | if n != 20 {
707 | b.Fatalf("want 20, got %d", n)
708 | }
709 | }
710 |
711 | func BenchmarkPrevFilteredUntilSelection(b *testing.B) {
712 | var n int
713 |
714 | b.StopTimer()
715 | sel := DocW().Find("h2")
716 | sel2 := DocW().Find("div")
717 | b.StartTimer()
718 | for i := 0; i < b.N; i++ {
719 | if n == 0 {
720 | n = sel.PrevFilteredUntilSelection("p", sel2).Length()
721 | } else {
722 | sel.PrevFilteredUntilSelection("p", sel2)
723 | }
724 | }
725 | if n != 20 {
726 | b.Fatalf("want 20, got %d", n)
727 | }
728 | }
729 |
730 | func BenchmarkPrevFilteredUntilNodes(b *testing.B) {
731 | var n int
732 |
733 | b.StopTimer()
734 | sel := DocW().Find("h2")
735 | sel2 := DocW().Find("div")
736 | nodes := sel2.Nodes
737 | b.StartTimer()
738 | for i := 0; i < b.N; i++ {
739 | if n == 0 {
740 | n = sel.PrevFilteredUntilNodes("p", nodes...).Length()
741 | } else {
742 | sel.PrevFilteredUntilNodes("p", nodes...)
743 | }
744 | }
745 | if n != 20 {
746 | b.Fatalf("want 20, got %d", n)
747 | }
748 | }
749 |
750 | func BenchmarkClosest(b *testing.B) {
751 | var n int
752 |
753 | b.StopTimer()
754 | sel := Doc().Find(".container-fluid")
755 | b.StartTimer()
756 | for i := 0; i < b.N; i++ {
757 | if n == 0 {
758 | n = sel.Closest(".pvk-content").Length()
759 | } else {
760 | sel.Closest(".pvk-content")
761 | }
762 | }
763 | if n != 2 {
764 | b.Fatalf("want 2, got %d", n)
765 | }
766 | }
767 |
768 | func BenchmarkClosestSelection(b *testing.B) {
769 | var n int
770 |
771 | b.StopTimer()
772 | sel := Doc().Find(".container-fluid")
773 | sel2 := Doc().Find(".pvk-content")
774 | b.StartTimer()
775 | for i := 0; i < b.N; i++ {
776 | if n == 0 {
777 | n = sel.ClosestSelection(sel2).Length()
778 | } else {
779 | sel.ClosestSelection(sel2)
780 | }
781 | }
782 | if n != 2 {
783 | b.Fatalf("want 2, got %d", n)
784 | }
785 | }
786 |
787 | func BenchmarkClosestNodes(b *testing.B) {
788 | var n int
789 |
790 | b.StopTimer()
791 | sel := Doc().Find(".container-fluid")
792 | nodes := Doc().Find(".pvk-content").Nodes
793 | b.StartTimer()
794 | for i := 0; i < b.N; i++ {
795 | if n == 0 {
796 | n = sel.ClosestNodes(nodes...).Length()
797 | } else {
798 | sel.ClosestNodes(nodes...)
799 | }
800 | }
801 | if n != 2 {
802 | b.Fatalf("want 2, got %d", n)
803 | }
804 | }
805 |
806 | func BenchmarkSingleMatcher(b *testing.B) {
807 | doc := Doc()
808 | multi := cascadia.MustCompile(`div`)
809 | single := SingleMatcher(multi)
810 | b.ResetTimer()
811 |
812 | b.Run("multi", func(b *testing.B) {
813 | for i := 0; i < b.N; i++ {
814 | _ = doc.FindMatcher(multi)
815 | }
816 | })
817 | b.Run("single", func(b *testing.B) {
818 | for i := 0; i < b.N; i++ {
819 | _ = doc.FindMatcher(single)
820 | }
821 | })
822 | }
823 |
--------------------------------------------------------------------------------
/doc.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2012-2016, Martin Angers & Contributors
2 | // All rights reserved.
3 | //
4 | // Redistribution and use in source and binary forms, with or without modification,
5 | // are permitted provided that the following conditions are met:
6 | //
7 | // * Redistributions of source code must retain the above copyright notice,
8 | // this list of conditions and the following disclaimer.
9 | // * Redistributions in binary form must reproduce the above copyright notice,
10 | // this list of conditions and the following disclaimer in the documentation and/or
11 | // other materials provided with the distribution.
12 | // * Neither the name of the author nor the names of its contributors may be used to
13 | // endorse or promote products derived from this software without specific prior written permission.
14 | //
15 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
16 | // OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
17 | // AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
18 | // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 | // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
21 | // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
22 | // WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 |
24 | /*
25 | Package goquery implements features similar to jQuery, including the chainable
26 | syntax, to manipulate and query an HTML document.
27 |
28 | It brings a syntax and a set of features similar to jQuery to the Go language.
29 | It is based on Go's net/html package and the CSS Selector library cascadia.
30 | Since the net/html parser returns nodes, and not a full-featured DOM
31 | tree, jQuery's stateful manipulation functions (like height(), css(), detach())
32 | have been left off.
33 |
34 | Also, because the net/html parser requires UTF-8 encoding, so does goquery: it is
35 | the caller's responsibility to ensure that the source document provides UTF-8 encoded HTML.
36 | See the repository's wiki for various options on how to do this.
37 |
38 | Syntax-wise, it is as close as possible to jQuery, with the same method names when
39 | possible, and that warm and fuzzy chainable interface. jQuery being the
40 | ultra-popular library that it is, writing a similar HTML-manipulating
41 | library was better to follow its API than to start anew (in the same spirit as
42 | Go's fmt package), even though some of its methods are less than intuitive (looking
43 | at you, index()...).
44 |
45 | It is hosted on GitHub, along with additional documentation in the README.md
46 | file: https://github.com/puerkitobio/goquery
47 |
48 | Please note that because of the net/html dependency, goquery requires Go1.1+.
49 |
50 | The various methods are split into files based on the category of behavior.
51 | The three dots (...) indicate that various "overloads" are available.
52 |
53 | * array.go : array-like positional manipulation of the selection.
54 | - Eq()
55 | - First()
56 | - Get()
57 | - Index...()
58 | - Last()
59 | - Slice()
60 |
61 | * expand.go : methods that expand or augment the selection's set.
62 | - Add...()
63 | - AndSelf()
64 | - Union(), which is an alias for AddSelection()
65 |
66 | * filter.go : filtering methods, that reduce the selection's set.
67 | - End()
68 | - Filter...()
69 | - Has...()
70 | - Intersection(), which is an alias of FilterSelection()
71 | - Not...()
72 |
73 | * iteration.go : methods to loop over the selection's nodes.
74 | - Each()
75 | - EachWithBreak()
76 | - Map()
77 |
78 | * manipulation.go : methods for modifying the document
79 | - After...()
80 | - Append...()
81 | - Before...()
82 | - Clone()
83 | - Empty()
84 | - Prepend...()
85 | - Remove...()
86 | - ReplaceWith...()
87 | - Unwrap()
88 | - Wrap...()
89 | - WrapAll...()
90 | - WrapInner...()
91 |
92 | * property.go : methods that inspect and get the node's properties values.
93 | - Attr*(), RemoveAttr(), SetAttr()
94 | - AddClass(), HasClass(), RemoveClass(), ToggleClass()
95 | - Html()
96 | - Length()
97 | - Size(), which is an alias for Length()
98 | - Text()
99 |
100 | * query.go : methods that query, or reflect, a node's identity.
101 | - Contains()
102 | - Is...()
103 |
104 | * traversal.go : methods to traverse the HTML document tree.
105 | - Children...()
106 | - Contents()
107 | - Find...()
108 | - Next...()
109 | - Parent[s]...()
110 | - Prev...()
111 | - Siblings...()
112 |
113 | * type.go : definition of the types exposed by goquery.
114 | - Document
115 | - Selection
116 | - Matcher
117 |
118 | * utilities.go : definition of helper functions (and not methods on a *Selection)
119 | that are not part of jQuery, but are useful to goquery.
120 | - NodeName
121 | - OuterHtml
122 | */
123 | package goquery
124 |
--------------------------------------------------------------------------------
/doc/tips.md:
--------------------------------------------------------------------------------
1 | # Tips and tricks
2 |
3 | ## Handle Non-UTF8 html Pages
4 |
5 | The `go.net/html` package used by `goquery` requires that the html document is UTF-8 encoded. When you know the encoding of the html page is not UTF-8, you can use the `iconv` package to convert it to UTF-8 (there are various implementation of the `iconv` API, see [godoc.org][iconv] for other options):
6 |
7 | ```bash
8 | $ go get -u github.com/djimenez/iconv-go
9 | ```
10 |
11 | and then:
12 |
13 | ```golang
14 | // Load the URL
15 | res, err := http.Get(url)
16 | if err != nil {
17 | // handle error
18 | }
19 | defer res.Body.Close()
20 |
21 | // Convert the designated charset HTML to utf-8 encoded HTML.
22 | // `charset` being one of the charsets known by the iconv package.
23 | utfBody, err := iconv.NewReader(res.Body, charset, "utf-8")
24 | if err != nil {
25 | // handler error
26 | }
27 |
28 | // use utfBody using goquery
29 | doc, err := goquery.NewDocumentFromReader(utfBody)
30 | if err != nil {
31 | // handler error
32 | }
33 | // use doc...
34 | ```
35 |
36 | Thanks to github user @YuheiNakasaka.
37 |
38 | Actually, the official go.text repository covers this use case too, see its [godoc page][text] for the details.
39 |
40 |
41 | ## Handle Javascript-based Pages
42 |
43 | `goquery` is great to handle normal html pages, but when most of the page is build dynamically using javascript, there's not much it can do. There are various options when faced with this problem:
44 |
45 | * Use a headless browser such as [webloop][].
46 | * Use a Go javascript parser package, such as [otto][].
47 |
48 | You can find a code example using `otto` [in this gist][exotto]. Thanks to github user @cryptix.
49 |
50 | ## For Loop
51 |
52 | If all you need is a normal `for` loop over all nodes in the current selection, where `Map/Each`-style iteration is not necessary, you can use the following:
53 |
54 | ```golang
55 | sel := Doc().Find(".selector")
56 | for i := range sel.Nodes {
57 | single := sel.Eq(i)
58 | // use `single` as a selection of 1 node
59 | }
60 | ```
61 |
62 | Thanks to github user @jmoiron.
63 |
64 | [webloop]: https://github.com/sourcegraph/webloop
65 | [otto]: https://github.com/robertkrimen/otto
66 | [exotto]: https://gist.github.com/cryptix/87127f76a94183747b53
67 | [iconv]: http://godoc.org/?q=iconv
68 | [text]: https://godoc.org/golang.org/x/text/encoding
69 |
--------------------------------------------------------------------------------
/example_test.go:
--------------------------------------------------------------------------------
1 | package goquery_test
2 |
3 | import (
4 | "fmt"
5 | "log"
6 | "net/http"
7 | "os"
8 | "strings"
9 |
10 | "github.com/PuerkitoBio/goquery"
11 | )
12 |
13 | // This example scrapes the reviews shown on the home page of metalsucks.net.
14 | func Example() {
15 | // Request the HTML page.
16 | res, err := http.Get("http://metalsucks.net")
17 | if err != nil {
18 | log.Fatal(err)
19 | }
20 | defer res.Body.Close()
21 | if res.StatusCode != 200 {
22 | log.Fatalf("status code error: %d %s", res.StatusCode, res.Status)
23 | }
24 |
25 | // Load the HTML document
26 | doc, err := goquery.NewDocumentFromReader(res.Body)
27 | if err != nil {
28 | log.Fatal(err)
29 | }
30 |
31 | // Find the review items
32 | doc.Find(".sidebar-reviews article .content-block").Each(func(i int, s *goquery.Selection) {
33 | // For each item found, get the band and title
34 | band := s.Find("a").Text()
35 | title := s.Find("i").Text()
36 | fmt.Printf("Review %d: %s - %s\n", i, band, title)
37 | })
38 | // To see the output of the Example while running the test suite (go test), simply
39 | // remove the leading "x" before Output on the next line. This will cause the
40 | // example to fail (all the "real" tests should pass).
41 |
42 | // xOutput: voluntarily fail the Example output.
43 | }
44 |
45 | // This example shows how to use NewDocumentFromReader from a file.
46 | func ExampleNewDocumentFromReader_file() {
47 | // create from a file
48 | f, err := os.Open("some/file.html")
49 | if err != nil {
50 | log.Fatal(err)
51 | }
52 | defer f.Close()
53 | doc, err := goquery.NewDocumentFromReader(f)
54 | if err != nil {
55 | log.Fatal(err)
56 | }
57 | // use the goquery document...
58 | _ = doc.Find("h1")
59 | }
60 |
61 | // This example shows how to use NewDocumentFromReader from a string.
62 | func ExampleNewDocumentFromReader_string() {
63 | // create from a string
64 | data := `
65 |
66 |
Some text
` 121 | d, err := NewDocumentFromReader(strings.NewReader(src)) 122 | if err != nil { 123 | t.Fatal(err) 124 | } 125 | txt := d.Find("p").Text() 126 | ix := strings.Index(txt, "\u00a0") 127 | if ix != 4 { 128 | t.Errorf("Text: expected a non-breaking space at index 4, got %d", ix) 129 | } 130 | 131 | h, err := d.Find("p").Html() 132 | if err != nil { 133 | t.Fatal(err) 134 | } 135 | ix = strings.Index(h, "\u00a0") 136 | if ix != 4 { 137 | t.Errorf("Html: expected a non-breaking space at index 4, got %d", ix) 138 | } 139 | } 140 | 141 | func TestAddClass(t *testing.T) { 142 | sel := Doc2Clone().Find("#main") 143 | sel.AddClass("main main main") 144 | 145 | // Make sure that class was only added once 146 | if a, ok := sel.Attr("class"); !ok || a != "main" { 147 | t.Error("Expected #main to have class main") 148 | } 149 | } 150 | 151 | func TestAddClassSimilar(t *testing.T) { 152 | sel := Doc2Clone().Find("#nf5") 153 | sel.AddClass("odd") 154 | 155 | assertClass(t, sel, "odd") 156 | assertClass(t, sel, "odder") 157 | printSel(t, sel.Parent()) 158 | } 159 | 160 | func TestAddEmptyClass(t *testing.T) { 161 | sel := Doc2Clone().Find("#main") 162 | sel.AddClass("") 163 | 164 | // Make sure that class was only added once 165 | if a, ok := sel.Attr("class"); ok { 166 | t.Errorf("Expected #main to not to have a class, have: %s", a) 167 | } 168 | } 169 | 170 | func TestAddClasses(t *testing.T) { 171 | sel := Doc2Clone().Find("#main") 172 | sel.AddClass("a b") 173 | 174 | // Make sure that class was only added once 175 | if !sel.HasClass("a") || !sel.HasClass("b") { 176 | t.Errorf("#main does not have classes") 177 | } 178 | } 179 | 180 | func TestHasClass(t *testing.T) { 181 | sel := Doc().Find("div") 182 | if !sel.HasClass("span12") { 183 | t.Error("Expected at least one div to have class span12.") 184 | } 185 | } 186 | 187 | func TestHasClassNone(t *testing.T) { 188 | sel := Doc().Find("h2") 189 | if sel.HasClass("toto") { 190 | t.Error("Expected h1 to have no class.") 191 | } 192 | } 193 | 194 | func TestHasClassNotFirst(t *testing.T) { 195 | sel := Doc().Find(".alert") 196 | if !sel.HasClass("alert-error") { 197 | t.Error("Expected .alert to also have class .alert-error.") 198 | } 199 | } 200 | 201 | func TestRemoveClass(t *testing.T) { 202 | sel := Doc2Clone().Find("#nf1") 203 | sel.RemoveClass("one row") 204 | 205 | if !sel.HasClass("even") || sel.HasClass("one") || sel.HasClass("row") { 206 | classes, _ := sel.Attr("class") 207 | t.Error("Expected #nf1 to have class even, has ", classes) 208 | } 209 | } 210 | 211 | func TestRemoveClassSimilar(t *testing.T) { 212 | sel := Doc2Clone().Find("#nf5, #nf6") 213 | assertLength(t, sel.Nodes, 2) 214 | 215 | sel.RemoveClass("odd") 216 | assertClass(t, sel.Eq(0), "odder") 217 | printSel(t, sel) 218 | } 219 | 220 | func TestRemoveAllClasses(t *testing.T) { 221 | sel := Doc2Clone().Find("#nf1") 222 | sel.RemoveClass() 223 | 224 | if a, ok := sel.Attr("class"); ok { 225 | t.Error("All classes were not removed, has ", a) 226 | } 227 | 228 | sel = Doc2Clone().Find("#main") 229 | sel.RemoveClass() 230 | if a, ok := sel.Attr("class"); ok { 231 | t.Error("All classes were not removed, has ", a) 232 | } 233 | } 234 | 235 | func TestToggleClass(t *testing.T) { 236 | sel := Doc2Clone().Find("#nf1") 237 | 238 | sel.ToggleClass("one") 239 | if sel.HasClass("one") { 240 | t.Error("Expected #nf1 to not have class one") 241 | } 242 | 243 | sel.ToggleClass("one") 244 | if !sel.HasClass("one") { 245 | t.Error("Expected #nf1 to have class one") 246 | } 247 | 248 | sel.ToggleClass("one even row") 249 | if a, ok := sel.Attr("class"); ok { 250 | t.Errorf("Expected #nf1 to have no classes, have %q", a) 251 | } 252 | } 253 | -------------------------------------------------------------------------------- /query.go: -------------------------------------------------------------------------------- 1 | package goquery 2 | 3 | import "golang.org/x/net/html" 4 | 5 | // Is checks the current matched set of elements against a selector and 6 | // returns true if at least one of these elements matches. 7 | func (s *Selection) Is(selector string) bool { 8 | return s.IsMatcher(compileMatcher(selector)) 9 | } 10 | 11 | // IsMatcher checks the current matched set of elements against a matcher and 12 | // returns true if at least one of these elements matches. 13 | func (s *Selection) IsMatcher(m Matcher) bool { 14 | if len(s.Nodes) > 0 { 15 | if len(s.Nodes) == 1 { 16 | return m.Match(s.Nodes[0]) 17 | } 18 | return len(m.Filter(s.Nodes)) > 0 19 | } 20 | 21 | return false 22 | } 23 | 24 | // IsFunction checks the current matched set of elements against a predicate and 25 | // returns true if at least one of these elements matches. 26 | func (s *Selection) IsFunction(f func(int, *Selection) bool) bool { 27 | return s.FilterFunction(f).Length() > 0 28 | } 29 | 30 | // IsSelection checks the current matched set of elements against a Selection object 31 | // and returns true if at least one of these elements matches. 32 | func (s *Selection) IsSelection(sel *Selection) bool { 33 | return s.FilterSelection(sel).Length() > 0 34 | } 35 | 36 | // IsNodes checks the current matched set of elements against the specified nodes 37 | // and returns true if at least one of these elements matches. 38 | func (s *Selection) IsNodes(nodes ...*html.Node) bool { 39 | return s.FilterNodes(nodes...).Length() > 0 40 | } 41 | 42 | // Contains returns true if the specified Node is within, 43 | // at any depth, one of the nodes in the Selection object. 44 | // It is NOT inclusive, to behave like jQuery's implementation, and 45 | // unlike Javascript's .contains, so if the contained 46 | // node is itself in the selection, it returns false. 47 | func (s *Selection) Contains(n *html.Node) bool { 48 | return sliceContains(s.Nodes, n) 49 | } 50 | -------------------------------------------------------------------------------- /query_test.go: -------------------------------------------------------------------------------- 1 | package goquery 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestIs(t *testing.T) { 8 | sel := Doc().Find(".footer p:nth-child(1)") 9 | if !sel.Is("p") { 10 | t.Error("Expected .footer p:nth-child(1) to be p.") 11 | } 12 | } 13 | 14 | func TestIsInvalid(t *testing.T) { 15 | sel := Doc().Find(".footer p:nth-child(1)") 16 | if sel.Is("") { 17 | t.Error("Is should not succeed with invalid selector string") 18 | } 19 | } 20 | 21 | func TestIsPositional(t *testing.T) { 22 | sel := Doc().Find(".footer p:nth-child(2)") 23 | if !sel.Is("p:nth-child(2)") { 24 | t.Error("Expected .footer p:nth-child(2) to be p:nth-child(2).") 25 | } 26 | } 27 | 28 | func TestIsPositionalNot(t *testing.T) { 29 | sel := Doc().Find(".footer p:nth-child(1)") 30 | if sel.Is("p:nth-child(2)") { 31 | t.Error("Expected .footer p:nth-child(1) NOT to be p:nth-child(2).") 32 | } 33 | } 34 | 35 | func TestIsFunction(t *testing.T) { 36 | ok := Doc().Find("div").IsFunction(func(i int, s *Selection) bool { 37 | return s.HasClass("container-fluid") 38 | }) 39 | 40 | if !ok { 41 | t.Error("Expected some div to have a container-fluid class.") 42 | } 43 | } 44 | 45 | func TestIsFunctionRollback(t *testing.T) { 46 | ok := Doc().Find("div").IsFunction(func(i int, s *Selection) bool { 47 | return s.HasClass("container-fluid") 48 | }) 49 | 50 | if !ok { 51 | t.Error("Expected some div to have a container-fluid class.") 52 | } 53 | } 54 | 55 | func TestIsSelection(t *testing.T) { 56 | sel := Doc().Find("div") 57 | sel2 := Doc().Find(".pvk-gutter") 58 | 59 | if !sel.IsSelection(sel2) { 60 | t.Error("Expected some div to have a pvk-gutter class.") 61 | } 62 | } 63 | 64 | func TestIsSelectionNot(t *testing.T) { 65 | sel := Doc().Find("div") 66 | sel2 := Doc().Find("a") 67 | 68 | if sel.IsSelection(sel2) { 69 | t.Error("Expected some div NOT to be an anchor.") 70 | } 71 | } 72 | 73 | func TestIsNodes(t *testing.T) { 74 | sel := Doc().Find("div") 75 | sel2 := Doc().Find(".footer") 76 | 77 | if !sel.IsNodes(sel2.Nodes[0]) { 78 | t.Error("Expected some div to have a footer class.") 79 | } 80 | } 81 | 82 | func TestDocContains(t *testing.T) { 83 | sel := Doc().Find("h1") 84 | if !Doc().Contains(sel.Nodes[0]) { 85 | t.Error("Expected document to contain H1 tag.") 86 | } 87 | } 88 | 89 | func TestSelContains(t *testing.T) { 90 | sel := Doc().Find(".row-fluid") 91 | sel2 := Doc().Find("a[ng-click]") 92 | if !sel.Contains(sel2.Nodes[0]) { 93 | t.Error("Expected .row-fluid to contain a[ng-click] tag.") 94 | } 95 | } 96 | 97 | func TestSelNotContains(t *testing.T) { 98 | sel := Doc().Find("a.link") 99 | sel2 := Doc().Find("span") 100 | if sel.Contains(sel2.Nodes[0]) { 101 | t.Error("Expected a.link to NOT contain span tag.") 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /testdata/page.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |31 | Prove your point. 32 |
33 |4
221 | 222 | 223 | ` 224 | doc, err := NewDocumentFromReader(strings.NewReader(data)) 225 | if err != nil { 226 | t.Fatal(err) 227 | } 228 | 229 | text := doc.FindMatcher(Single("div")).Text() 230 | if text != "1" { 231 | t.Fatalf("want %q, got %q", "1", text) 232 | } 233 | 234 | // Verify semantic equivalence 235 | sel1 := doc.Find("div").First() 236 | sel2 := doc.FindMatcher(Single("div")) 237 | if sel1.Text() != sel2.Text() { 238 | t.Fatalf("want sel1 to equal sel2") 239 | } 240 | 241 | // Here, the Single has no effect as the selector is used to filter 242 | // from the existing selection, not to find nodes in the document. 243 | divs := doc.Find("div") 244 | text = divs.FilterMatcher(Single(".a")).Text() 245 | if text != "23" { 246 | t.Fatalf("want %q, got %q", "23", text) 247 | } 248 | 249 | classA := cascadia.MustCompile(".a") 250 | classB := cascadia.MustCompile(".b") 251 | text = doc.FindMatcher(classB).AddMatcher(SingleMatcher(classA)).Text() 252 | if text != "142" { 253 | t.Fatalf("want %q, got %q", "142", text) 254 | } 255 | } 256 | -------------------------------------------------------------------------------- /utilities.go: -------------------------------------------------------------------------------- 1 | package goquery 2 | 3 | import ( 4 | "io" 5 | "strings" 6 | 7 | "golang.org/x/net/html" 8 | ) 9 | 10 | // used to determine if a set (map[*html.Node]bool) should be used 11 | // instead of iterating over a slice. The set uses more memory and 12 | // is slower than slice iteration for small N. 13 | const minNodesForSet = 1000 14 | 15 | var nodeNames = []string{ 16 | html.ErrorNode: "#error", 17 | html.TextNode: "#text", 18 | html.DocumentNode: "#document", 19 | html.CommentNode: "#comment", 20 | } 21 | 22 | // NodeName returns the node name of the first element in the selection. 23 | // It tries to behave in a similar way as the DOM's nodeName property 24 | // (https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeName). 25 | // 26 | // Go's net/html package defines the following node types, listed with 27 | // the corresponding returned value from this function: 28 | // 29 | // ErrorNode : #error 30 | // TextNode : #text 31 | // DocumentNode : #document 32 | // ElementNode : the element's tag name 33 | // CommentNode : #comment 34 | // DoctypeNode : the name of the document type 35 | func NodeName(s *Selection) string { 36 | if s.Length() == 0 { 37 | return "" 38 | } 39 | return nodeName(s.Get(0)) 40 | } 41 | 42 | // nodeName returns the node name of the given html node. 43 | // See NodeName for additional details on behaviour. 44 | func nodeName(node *html.Node) string { 45 | if node == nil { 46 | return "" 47 | } 48 | 49 | switch node.Type { 50 | case html.ElementNode, html.DoctypeNode: 51 | return node.Data 52 | default: 53 | if int(node.Type) < len(nodeNames) { 54 | return nodeNames[node.Type] 55 | } 56 | return "" 57 | } 58 | } 59 | 60 | // Render renders the HTML of the first item in the selection and writes it to 61 | // the writer. It behaves the same as OuterHtml but writes to w instead of 62 | // returning the string. 63 | func Render(w io.Writer, s *Selection) error { 64 | if s.Length() == 0 { 65 | return nil 66 | } 67 | n := s.Get(0) 68 | return html.Render(w, n) 69 | } 70 | 71 | // OuterHtml returns the outer HTML rendering of the first item in 72 | // the selection - that is, the HTML including the first element's 73 | // tag and attributes. 74 | // 75 | // Unlike Html, this is a function and not a method on the Selection, 76 | // because this is not a jQuery method (in javascript-land, this is 77 | // a property provided by the DOM). 78 | func OuterHtml(s *Selection) (string, error) { 79 | var builder strings.Builder 80 | if err := Render(&builder, s); err != nil { 81 | return "", err 82 | } 83 | return builder.String(), nil 84 | } 85 | 86 | // Loop through all container nodes to search for the target node. 87 | func sliceContains(container []*html.Node, contained *html.Node) bool { 88 | for _, n := range container { 89 | if nodeContains(n, contained) { 90 | return true 91 | } 92 | } 93 | 94 | return false 95 | } 96 | 97 | // Checks if the contained node is within the container node. 98 | func nodeContains(container *html.Node, contained *html.Node) bool { 99 | // Check if the parent of the contained node is the container node, traversing 100 | // upward until the top is reached, or the container is found. 101 | for contained = contained.Parent; contained != nil; contained = contained.Parent { 102 | if container == contained { 103 | return true 104 | } 105 | } 106 | return false 107 | } 108 | 109 | // Checks if the target node is in the slice of nodes. 110 | func isInSlice(slice []*html.Node, node *html.Node) bool { 111 | return indexInSlice(slice, node) > -1 112 | } 113 | 114 | // Returns the index of the target node in the slice, or -1. 115 | func indexInSlice(slice []*html.Node, node *html.Node) int { 116 | if node != nil { 117 | for i, n := range slice { 118 | if n == node { 119 | return i 120 | } 121 | } 122 | } 123 | return -1 124 | } 125 | 126 | // Appends the new nodes to the target slice, making sure no duplicate is added. 127 | // There is no check to the original state of the target slice, so it may still 128 | // contain duplicates. The target slice is returned because append() may create 129 | // a new underlying array. If targetSet is nil, a local set is created with the 130 | // target if len(target) + len(nodes) is greater than minNodesForSet. 131 | func appendWithoutDuplicates(target []*html.Node, nodes []*html.Node, targetSet map[*html.Node]bool) []*html.Node { 132 | // if there are not that many nodes, don't use the map, faster to just use nested loops 133 | // (unless a non-nil targetSet is passed, in which case the caller knows better). 134 | if targetSet == nil && len(target)+len(nodes) < minNodesForSet { 135 | for _, n := range nodes { 136 | if !isInSlice(target, n) { 137 | target = append(target, n) 138 | } 139 | } 140 | return target 141 | } 142 | 143 | // if a targetSet is passed, then assume it is reliable, otherwise create one 144 | // and initialize it with the current target contents. 145 | if targetSet == nil { 146 | targetSet = make(map[*html.Node]bool, len(target)) 147 | for _, n := range target { 148 | targetSet[n] = true 149 | } 150 | } 151 | for _, n := range nodes { 152 | if !targetSet[n] { 153 | target = append(target, n) 154 | targetSet[n] = true 155 | } 156 | } 157 | 158 | return target 159 | } 160 | 161 | // Loop through a selection, returning only those nodes that pass the predicate 162 | // function. 163 | func grep(sel *Selection, predicate func(i int, s *Selection) bool) (result []*html.Node) { 164 | for i, n := range sel.Nodes { 165 | if predicate(i, newSingleSelection(n, sel.document)) { 166 | result = append(result, n) 167 | } 168 | } 169 | return result 170 | } 171 | 172 | // Creates a new Selection object based on the specified nodes, and keeps the 173 | // source Selection object on the stack (linked list). 174 | func pushStack(fromSel *Selection, nodes []*html.Node) *Selection { 175 | result := &Selection{nodes, fromSel.document, fromSel} 176 | return result 177 | } 178 | -------------------------------------------------------------------------------- /utilities_test.go: -------------------------------------------------------------------------------- 1 | package goquery 2 | 3 | import ( 4 | "reflect" 5 | "sort" 6 | "strings" 7 | "testing" 8 | 9 | "golang.org/x/net/html" 10 | ) 11 | 12 | var allNodes = ` 13 | 14 | 15 | 16 | 17 | 18 |19 | This is some text. 20 |
21 | 22 | 23 | 24 | 25 | ` 26 | 27 | func TestNodeName(t *testing.T) { 28 | doc, err := NewDocumentFromReader(strings.NewReader(allNodes)) 29 | if err != nil { 30 | t.Fatal(err) 31 | } 32 | 33 | n0 := doc.Nodes[0] 34 | nDT := n0.FirstChild 35 | sMeta := doc.Find("meta") 36 | nMeta := sMeta.Get(0) 37 | sP := doc.Find("p") 38 | nP := sP.Get(0) 39 | nComment := nP.FirstChild 40 | nText := nComment.NextSibling 41 | 42 | cases := []struct { 43 | node *html.Node 44 | typ html.NodeType 45 | want string 46 | }{ 47 | {n0, html.DocumentNode, nodeNames[html.DocumentNode]}, 48 | {nDT, html.DoctypeNode, "html"}, 49 | {nMeta, html.ElementNode, "meta"}, 50 | {nP, html.ElementNode, "p"}, 51 | {nComment, html.CommentNode, nodeNames[html.CommentNode]}, 52 | {nText, html.TextNode, nodeNames[html.TextNode]}, 53 | } 54 | for i, c := range cases { 55 | got := NodeName(newSingleSelection(c.node, doc)) 56 | if c.node.Type != c.typ { 57 | t.Errorf("%d: want type %v, got %v", i, c.typ, c.node.Type) 58 | } 59 | if got != c.want { 60 | t.Errorf("%d: want %q, got %q", i, c.want, got) 61 | } 62 | } 63 | } 64 | 65 | func TestNodeNameMultiSel(t *testing.T) { 66 | doc, err := NewDocumentFromReader(strings.NewReader(allNodes)) 67 | if err != nil { 68 | t.Fatal(err) 69 | } 70 | 71 | in := []string{"p", "h1", "div"} 72 | var out []string 73 | doc.Find(strings.Join(in, ", ")).Each(func(i int, s *Selection) { 74 | got := NodeName(s) 75 | out = append(out, got) 76 | }) 77 | sort.Strings(in) 78 | sort.Strings(out) 79 | if !reflect.DeepEqual(in, out) { 80 | t.Errorf("want %v, got %v", in, out) 81 | } 82 | } 83 | 84 | func TestOuterHtml(t *testing.T) { 85 | doc, err := NewDocumentFromReader(strings.NewReader(allNodes)) 86 | if err != nil { 87 | t.Fatal(err) 88 | } 89 | 90 | n0 := doc.Nodes[0] 91 | nDT := n0.FirstChild 92 | sMeta := doc.Find("meta") 93 | sP := doc.Find("p") 94 | nP := sP.Get(0) 95 | nComment := nP.FirstChild 96 | nText := nComment.NextSibling 97 | sHeaders := doc.Find(".header") 98 | 99 | cases := []struct { 100 | node *html.Node 101 | sel *Selection 102 | want string 103 | }{ 104 | {nDT, nil, ""}, // render makes DOCTYPE all caps 105 | {nil, sMeta, ``}, // and auto-closes the meta 106 | {nil, sP, `107 | This is some text. 108 |
`}, 109 | {nComment, nil, ""}, 110 | {nText, nil, ` 111 | This is some text. 112 | `}, 113 | {nil, sHeaders, ``}, 114 | } 115 | for i, c := range cases { 116 | if c.sel == nil { 117 | c.sel = newSingleSelection(c.node, doc) 118 | } 119 | got, err := OuterHtml(c.sel) 120 | if err != nil { 121 | t.Fatal(err) 122 | } 123 | 124 | if got != c.want { 125 | t.Errorf("%d: want %q, got %q", i, c.want, got) 126 | } 127 | } 128 | } 129 | --------------------------------------------------------------------------------