You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

manipulation.go 18KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573
  1. package goquery
  2. import (
  3. "strings"
  4. "golang.org/x/net/html"
  5. )
  6. // After applies the selector from the root document and inserts the matched elements
  7. // after the elements in the set of matched elements.
  8. //
  9. // If one of the matched elements in the selection is not currently in the
  10. // document, it's impossible to insert nodes after it, so it will be ignored.
  11. //
  12. // This follows the same rules as Selection.Append.
  13. func (s *Selection) After(selector string) *Selection {
  14. return s.AfterMatcher(compileMatcher(selector))
  15. }
  16. // AfterMatcher applies the matcher from the root document and inserts the matched elements
  17. // after the elements in the set of matched elements.
  18. //
  19. // If one of the matched elements in the selection is not currently in the
  20. // document, it's impossible to insert nodes after it, so it will be ignored.
  21. //
  22. // This follows the same rules as Selection.Append.
  23. func (s *Selection) AfterMatcher(m Matcher) *Selection {
  24. return s.AfterNodes(m.MatchAll(s.document.rootNode)...)
  25. }
  26. // AfterSelection inserts the elements in the selection after each element in the set of matched
  27. // elements.
  28. //
  29. // This follows the same rules as Selection.Append.
  30. func (s *Selection) AfterSelection(sel *Selection) *Selection {
  31. return s.AfterNodes(sel.Nodes...)
  32. }
  33. // AfterHtml parses the html and inserts it after the set of matched elements.
  34. //
  35. // This follows the same rules as Selection.Append.
  36. func (s *Selection) AfterHtml(html string) *Selection {
  37. return s.AfterNodes(parseHtml(html)...)
  38. }
  39. // AfterNodes inserts the nodes after each element in the set of matched elements.
  40. //
  41. // This follows the same rules as Selection.Append.
  42. func (s *Selection) AfterNodes(ns ...*html.Node) *Selection {
  43. return s.manipulateNodes(ns, true, func(sn *html.Node, n *html.Node) {
  44. if sn.Parent != nil {
  45. sn.Parent.InsertBefore(n, sn.NextSibling)
  46. }
  47. })
  48. }
  49. // Append appends the elements specified by the selector to the end of each element
  50. // in the set of matched elements, following those rules:
  51. //
  52. // 1) The selector is applied to the root document.
  53. //
  54. // 2) Elements that are part of the document will be moved to the new location.
  55. //
  56. // 3) If there are multiple locations to append to, cloned nodes will be
  57. // appended to all target locations except the last one, which will be moved
  58. // as noted in (2).
  59. func (s *Selection) Append(selector string) *Selection {
  60. return s.AppendMatcher(compileMatcher(selector))
  61. }
  62. // AppendMatcher appends the elements specified by the matcher to the end of each element
  63. // in the set of matched elements.
  64. //
  65. // This follows the same rules as Selection.Append.
  66. func (s *Selection) AppendMatcher(m Matcher) *Selection {
  67. return s.AppendNodes(m.MatchAll(s.document.rootNode)...)
  68. }
  69. // AppendSelection appends the elements in the selection to the end of each element
  70. // in the set of matched elements.
  71. //
  72. // This follows the same rules as Selection.Append.
  73. func (s *Selection) AppendSelection(sel *Selection) *Selection {
  74. return s.AppendNodes(sel.Nodes...)
  75. }
  76. // AppendHtml parses the html and appends it to the set of matched elements.
  77. func (s *Selection) AppendHtml(html string) *Selection {
  78. return s.AppendNodes(parseHtml(html)...)
  79. }
  80. // AppendNodes appends the specified nodes to each node in the set of matched elements.
  81. //
  82. // This follows the same rules as Selection.Append.
  83. func (s *Selection) AppendNodes(ns ...*html.Node) *Selection {
  84. return s.manipulateNodes(ns, false, func(sn *html.Node, n *html.Node) {
  85. sn.AppendChild(n)
  86. })
  87. }
  88. // Before inserts the matched elements before each element in the set of matched elements.
  89. //
  90. // This follows the same rules as Selection.Append.
  91. func (s *Selection) Before(selector string) *Selection {
  92. return s.BeforeMatcher(compileMatcher(selector))
  93. }
  94. // BeforeMatcher inserts the matched elements before each element in the set of matched elements.
  95. //
  96. // This follows the same rules as Selection.Append.
  97. func (s *Selection) BeforeMatcher(m Matcher) *Selection {
  98. return s.BeforeNodes(m.MatchAll(s.document.rootNode)...)
  99. }
  100. // BeforeSelection inserts the elements in the selection before each element in the set of matched
  101. // elements.
  102. //
  103. // This follows the same rules as Selection.Append.
  104. func (s *Selection) BeforeSelection(sel *Selection) *Selection {
  105. return s.BeforeNodes(sel.Nodes...)
  106. }
  107. // BeforeHtml parses the html and inserts it before the set of matched elements.
  108. //
  109. // This follows the same rules as Selection.Append.
  110. func (s *Selection) BeforeHtml(html string) *Selection {
  111. return s.BeforeNodes(parseHtml(html)...)
  112. }
  113. // BeforeNodes inserts the nodes before each element in the set of matched elements.
  114. //
  115. // This follows the same rules as Selection.Append.
  116. func (s *Selection) BeforeNodes(ns ...*html.Node) *Selection {
  117. return s.manipulateNodes(ns, false, func(sn *html.Node, n *html.Node) {
  118. if sn.Parent != nil {
  119. sn.Parent.InsertBefore(n, sn)
  120. }
  121. })
  122. }
  123. // Clone creates a deep copy of the set of matched nodes. The new nodes will not be
  124. // attached to the document.
  125. func (s *Selection) Clone() *Selection {
  126. ns := newEmptySelection(s.document)
  127. ns.Nodes = cloneNodes(s.Nodes)
  128. return ns
  129. }
  130. // Empty removes all children nodes from the set of matched elements.
  131. // It returns the children nodes in a new Selection.
  132. func (s *Selection) Empty() *Selection {
  133. var nodes []*html.Node
  134. for _, n := range s.Nodes {
  135. for c := n.FirstChild; c != nil; c = n.FirstChild {
  136. n.RemoveChild(c)
  137. nodes = append(nodes, c)
  138. }
  139. }
  140. return pushStack(s, nodes)
  141. }
  142. // Prepend prepends the elements specified by the selector to each element in
  143. // the set of matched elements, following the same rules as Append.
  144. func (s *Selection) Prepend(selector string) *Selection {
  145. return s.PrependMatcher(compileMatcher(selector))
  146. }
  147. // PrependMatcher prepends the elements specified by the matcher to each
  148. // element in the set of matched elements.
  149. //
  150. // This follows the same rules as Selection.Append.
  151. func (s *Selection) PrependMatcher(m Matcher) *Selection {
  152. return s.PrependNodes(m.MatchAll(s.document.rootNode)...)
  153. }
  154. // PrependSelection prepends the elements in the selection to each element in
  155. // the set of matched elements.
  156. //
  157. // This follows the same rules as Selection.Append.
  158. func (s *Selection) PrependSelection(sel *Selection) *Selection {
  159. return s.PrependNodes(sel.Nodes...)
  160. }
  161. // PrependHtml parses the html and prepends it to the set of matched elements.
  162. func (s *Selection) PrependHtml(html string) *Selection {
  163. return s.PrependNodes(parseHtml(html)...)
  164. }
  165. // PrependNodes prepends the specified nodes to each node in the set of
  166. // matched elements.
  167. //
  168. // This follows the same rules as Selection.Append.
  169. func (s *Selection) PrependNodes(ns ...*html.Node) *Selection {
  170. return s.manipulateNodes(ns, true, func(sn *html.Node, n *html.Node) {
  171. // sn.FirstChild may be nil, in which case this functions like
  172. // sn.AppendChild()
  173. sn.InsertBefore(n, sn.FirstChild)
  174. })
  175. }
  176. // Remove removes the set of matched elements from the document.
  177. // It returns the same selection, now consisting of nodes not in the document.
  178. func (s *Selection) Remove() *Selection {
  179. for _, n := range s.Nodes {
  180. if n.Parent != nil {
  181. n.Parent.RemoveChild(n)
  182. }
  183. }
  184. return s
  185. }
  186. // RemoveFiltered removes the set of matched elements by selector.
  187. // It returns the Selection of removed nodes.
  188. func (s *Selection) RemoveFiltered(selector string) *Selection {
  189. return s.RemoveMatcher(compileMatcher(selector))
  190. }
  191. // RemoveMatcher removes the set of matched elements.
  192. // It returns the Selection of removed nodes.
  193. func (s *Selection) RemoveMatcher(m Matcher) *Selection {
  194. return s.FilterMatcher(m).Remove()
  195. }
  196. // ReplaceWith replaces each element in the set of matched elements with the
  197. // nodes matched by the given selector.
  198. // It returns the removed elements.
  199. //
  200. // This follows the same rules as Selection.Append.
  201. func (s *Selection) ReplaceWith(selector string) *Selection {
  202. return s.ReplaceWithMatcher(compileMatcher(selector))
  203. }
  204. // ReplaceWithMatcher replaces each element in the set of matched elements with
  205. // the nodes matched by the given Matcher.
  206. // It returns the removed elements.
  207. //
  208. // This follows the same rules as Selection.Append.
  209. func (s *Selection) ReplaceWithMatcher(m Matcher) *Selection {
  210. return s.ReplaceWithNodes(m.MatchAll(s.document.rootNode)...)
  211. }
  212. // ReplaceWithSelection replaces each element in the set of matched elements with
  213. // the nodes from the given Selection.
  214. // It returns the removed elements.
  215. //
  216. // This follows the same rules as Selection.Append.
  217. func (s *Selection) ReplaceWithSelection(sel *Selection) *Selection {
  218. return s.ReplaceWithNodes(sel.Nodes...)
  219. }
  220. // ReplaceWithHtml replaces each element in the set of matched elements with
  221. // the parsed HTML.
  222. // It returns the removed elements.
  223. //
  224. // This follows the same rules as Selection.Append.
  225. func (s *Selection) ReplaceWithHtml(html string) *Selection {
  226. return s.ReplaceWithNodes(parseHtml(html)...)
  227. }
  228. // ReplaceWithNodes replaces each element in the set of matched elements with
  229. // the given nodes.
  230. // It returns the removed elements.
  231. //
  232. // This follows the same rules as Selection.Append.
  233. func (s *Selection) ReplaceWithNodes(ns ...*html.Node) *Selection {
  234. s.AfterNodes(ns...)
  235. return s.Remove()
  236. }
  237. // Set the html content of each element in the selection to specified html string.
  238. func (s *Selection) SetHtml(html string) *Selection {
  239. return setHtmlNodes(s, parseHtml(html)...)
  240. }
  241. // Set the content of each element in the selection to specified content. The
  242. // provided text string is escaped.
  243. func (s *Selection) SetText(text string) *Selection {
  244. return s.SetHtml(html.EscapeString(text))
  245. }
  246. // Unwrap removes the parents of the set of matched elements, leaving the matched
  247. // elements (and their siblings, if any) in their place.
  248. // It returns the original selection.
  249. func (s *Selection) Unwrap() *Selection {
  250. s.Parent().Each(func(i int, ss *Selection) {
  251. // For some reason, jquery allows unwrap to remove the <head> element, so
  252. // allowing it here too. Same for <html>. Why it allows those elements to
  253. // be unwrapped while not allowing body is a mystery to me.
  254. if ss.Nodes[0].Data != "body" {
  255. ss.ReplaceWithSelection(ss.Contents())
  256. }
  257. })
  258. return s
  259. }
  260. // Wrap wraps each element in the set of matched elements inside the first
  261. // element matched by the given selector. The matched child is cloned before
  262. // being inserted into the document.
  263. //
  264. // It returns the original set of elements.
  265. func (s *Selection) Wrap(selector string) *Selection {
  266. return s.WrapMatcher(compileMatcher(selector))
  267. }
  268. // WrapMatcher wraps each element in the set of matched elements inside the
  269. // first element matched by the given matcher. The matched child is cloned
  270. // before being inserted into the document.
  271. //
  272. // It returns the original set of elements.
  273. func (s *Selection) WrapMatcher(m Matcher) *Selection {
  274. return s.wrapNodes(m.MatchAll(s.document.rootNode)...)
  275. }
  276. // WrapSelection wraps each element in the set of matched elements inside the
  277. // first element in the given Selection. The element is cloned before being
  278. // inserted into the document.
  279. //
  280. // It returns the original set of elements.
  281. func (s *Selection) WrapSelection(sel *Selection) *Selection {
  282. return s.wrapNodes(sel.Nodes...)
  283. }
  284. // WrapHtml wraps each element in the set of matched elements inside the inner-
  285. // most child of the given HTML.
  286. //
  287. // It returns the original set of elements.
  288. func (s *Selection) WrapHtml(html string) *Selection {
  289. return s.wrapNodes(parseHtml(html)...)
  290. }
  291. // WrapNode wraps each element in the set of matched elements inside the inner-
  292. // most child of the given node. The given node is copied before being inserted
  293. // into the document.
  294. //
  295. // It returns the original set of elements.
  296. func (s *Selection) WrapNode(n *html.Node) *Selection {
  297. return s.wrapNodes(n)
  298. }
  299. func (s *Selection) wrapNodes(ns ...*html.Node) *Selection {
  300. s.Each(func(i int, ss *Selection) {
  301. ss.wrapAllNodes(ns...)
  302. })
  303. return s
  304. }
  305. // WrapAll wraps a single HTML structure, matched by the given selector, around
  306. // all elements in the set of matched elements. The matched child is cloned
  307. // before being inserted into the document.
  308. //
  309. // It returns the original set of elements.
  310. func (s *Selection) WrapAll(selector string) *Selection {
  311. return s.WrapAllMatcher(compileMatcher(selector))
  312. }
  313. // WrapAllMatcher wraps a single HTML structure, matched by the given Matcher,
  314. // around all elements in the set of matched elements. The matched child is
  315. // cloned before being inserted into the document.
  316. //
  317. // It returns the original set of elements.
  318. func (s *Selection) WrapAllMatcher(m Matcher) *Selection {
  319. return s.wrapAllNodes(m.MatchAll(s.document.rootNode)...)
  320. }
  321. // WrapAllSelection wraps a single HTML structure, the first node of the given
  322. // Selection, around all elements in the set of matched elements. The matched
  323. // child is cloned before being inserted into the document.
  324. //
  325. // It returns the original set of elements.
  326. func (s *Selection) WrapAllSelection(sel *Selection) *Selection {
  327. return s.wrapAllNodes(sel.Nodes...)
  328. }
  329. // WrapAllHtml wraps the given HTML structure around all elements in the set of
  330. // matched elements. The matched child is cloned before being inserted into the
  331. // document.
  332. //
  333. // It returns the original set of elements.
  334. func (s *Selection) WrapAllHtml(html string) *Selection {
  335. return s.wrapAllNodes(parseHtml(html)...)
  336. }
  337. func (s *Selection) wrapAllNodes(ns ...*html.Node) *Selection {
  338. if len(ns) > 0 {
  339. return s.WrapAllNode(ns[0])
  340. }
  341. return s
  342. }
  343. // WrapAllNode wraps the given node around the first element in the Selection,
  344. // making all other nodes in the Selection children of the given node. The node
  345. // is cloned before being inserted into the document.
  346. //
  347. // It returns the original set of elements.
  348. func (s *Selection) WrapAllNode(n *html.Node) *Selection {
  349. if s.Size() == 0 {
  350. return s
  351. }
  352. wrap := cloneNode(n)
  353. first := s.Nodes[0]
  354. if first.Parent != nil {
  355. first.Parent.InsertBefore(wrap, first)
  356. first.Parent.RemoveChild(first)
  357. }
  358. for c := getFirstChildEl(wrap); c != nil; c = getFirstChildEl(wrap) {
  359. wrap = c
  360. }
  361. newSingleSelection(wrap, s.document).AppendSelection(s)
  362. return s
  363. }
  364. // WrapInner wraps an HTML structure, matched by the given selector, around the
  365. // content of element in the set of matched elements. The matched child is
  366. // cloned before being inserted into the document.
  367. //
  368. // It returns the original set of elements.
  369. func (s *Selection) WrapInner(selector string) *Selection {
  370. return s.WrapInnerMatcher(compileMatcher(selector))
  371. }
  372. // WrapInnerMatcher wraps an HTML structure, matched by the given selector,
  373. // around the content of element in the set of matched elements. The matched
  374. // child is cloned before being inserted into the document.
  375. //
  376. // It returns the original set of elements.
  377. func (s *Selection) WrapInnerMatcher(m Matcher) *Selection {
  378. return s.wrapInnerNodes(m.MatchAll(s.document.rootNode)...)
  379. }
  380. // WrapInnerSelection wraps an HTML structure, matched by the given selector,
  381. // around the content of element in the set of matched elements. The matched
  382. // child is cloned before being inserted into the document.
  383. //
  384. // It returns the original set of elements.
  385. func (s *Selection) WrapInnerSelection(sel *Selection) *Selection {
  386. return s.wrapInnerNodes(sel.Nodes...)
  387. }
  388. // WrapInnerHtml wraps an HTML structure, matched by the given selector, around
  389. // the content of element in the set of matched elements. The matched child is
  390. // cloned before being inserted into the document.
  391. //
  392. // It returns the original set of elements.
  393. func (s *Selection) WrapInnerHtml(html string) *Selection {
  394. return s.wrapInnerNodes(parseHtml(html)...)
  395. }
  396. // WrapInnerNode wraps an HTML structure, matched by the given selector, around
  397. // the content of element in the set of matched elements. The matched child is
  398. // cloned before being inserted into the document.
  399. //
  400. // It returns the original set of elements.
  401. func (s *Selection) WrapInnerNode(n *html.Node) *Selection {
  402. return s.wrapInnerNodes(n)
  403. }
  404. func (s *Selection) wrapInnerNodes(ns ...*html.Node) *Selection {
  405. if len(ns) == 0 {
  406. return s
  407. }
  408. s.Each(func(i int, s *Selection) {
  409. contents := s.Contents()
  410. if contents.Size() > 0 {
  411. contents.wrapAllNodes(ns...)
  412. } else {
  413. s.AppendNodes(cloneNode(ns[0]))
  414. }
  415. })
  416. return s
  417. }
  418. func parseHtml(h string) []*html.Node {
  419. // Errors are only returned when the io.Reader returns any error besides
  420. // EOF, but strings.Reader never will
  421. nodes, err := html.ParseFragment(strings.NewReader(h), &html.Node{Type: html.ElementNode})
  422. if err != nil {
  423. panic("goquery: failed to parse HTML: " + err.Error())
  424. }
  425. return nodes
  426. }
  427. func setHtmlNodes(s *Selection, ns ...*html.Node) *Selection {
  428. for _, n := range s.Nodes {
  429. for c := n.FirstChild; c != nil; c = n.FirstChild {
  430. n.RemoveChild(c)
  431. }
  432. for _, c := range ns {
  433. n.AppendChild(cloneNode(c))
  434. }
  435. }
  436. return s
  437. }
  438. // Get the first child that is an ElementNode
  439. func getFirstChildEl(n *html.Node) *html.Node {
  440. c := n.FirstChild
  441. for c != nil && c.Type != html.ElementNode {
  442. c = c.NextSibling
  443. }
  444. return c
  445. }
  446. // Deep copy a slice of nodes.
  447. func cloneNodes(ns []*html.Node) []*html.Node {
  448. cns := make([]*html.Node, 0, len(ns))
  449. for _, n := range ns {
  450. cns = append(cns, cloneNode(n))
  451. }
  452. return cns
  453. }
  454. // Deep copy a node. The new node has clones of all the original node's
  455. // children but none of its parents or siblings.
  456. func cloneNode(n *html.Node) *html.Node {
  457. nn := &html.Node{
  458. Type: n.Type,
  459. DataAtom: n.DataAtom,
  460. Data: n.Data,
  461. Attr: make([]html.Attribute, len(n.Attr)),
  462. }
  463. copy(nn.Attr, n.Attr)
  464. for c := n.FirstChild; c != nil; c = c.NextSibling {
  465. nn.AppendChild(cloneNode(c))
  466. }
  467. return nn
  468. }
  469. func (s *Selection) manipulateNodes(ns []*html.Node, reverse bool,
  470. f func(sn *html.Node, n *html.Node)) *Selection {
  471. lasti := s.Size() - 1
  472. // net.Html doesn't provide document fragments for insertion, so to get
  473. // things in the correct order with After() and Prepend(), the callback
  474. // needs to be called on the reverse of the nodes.
  475. if reverse {
  476. for i, j := 0, len(ns)-1; i < j; i, j = i+1, j-1 {
  477. ns[i], ns[j] = ns[j], ns[i]
  478. }
  479. }
  480. for i, sn := range s.Nodes {
  481. for _, n := range ns {
  482. if i != lasti {
  483. f(sn, cloneNode(n))
  484. } else {
  485. if n.Parent != nil {
  486. n.Parent.RemoveChild(n)
  487. }
  488. f(sn, n)
  489. }
  490. }
  491. }
  492. return s
  493. }