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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574
  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. // SetHtml sets the html content of each element in the selection to
  238. // specified html string.
  239. func (s *Selection) SetHtml(html string) *Selection {
  240. return setHtmlNodes(s, parseHtml(html)...)
  241. }
  242. // SetText sets the content of each element in the selection to specified content.
  243. // The provided text string is escaped.
  244. func (s *Selection) SetText(text string) *Selection {
  245. return s.SetHtml(html.EscapeString(text))
  246. }
  247. // Unwrap removes the parents of the set of matched elements, leaving the matched
  248. // elements (and their siblings, if any) in their place.
  249. // It returns the original selection.
  250. func (s *Selection) Unwrap() *Selection {
  251. s.Parent().Each(func(i int, ss *Selection) {
  252. // For some reason, jquery allows unwrap to remove the <head> element, so
  253. // allowing it here too. Same for <html>. Why it allows those elements to
  254. // be unwrapped while not allowing body is a mystery to me.
  255. if ss.Nodes[0].Data != "body" {
  256. ss.ReplaceWithSelection(ss.Contents())
  257. }
  258. })
  259. return s
  260. }
  261. // Wrap wraps each element in the set of matched elements inside the first
  262. // element matched by the given selector. The matched child is cloned before
  263. // being inserted into the document.
  264. //
  265. // It returns the original set of elements.
  266. func (s *Selection) Wrap(selector string) *Selection {
  267. return s.WrapMatcher(compileMatcher(selector))
  268. }
  269. // WrapMatcher wraps each element in the set of matched elements inside the
  270. // first element matched by the given matcher. The matched child is cloned
  271. // before being inserted into the document.
  272. //
  273. // It returns the original set of elements.
  274. func (s *Selection) WrapMatcher(m Matcher) *Selection {
  275. return s.wrapNodes(m.MatchAll(s.document.rootNode)...)
  276. }
  277. // WrapSelection wraps each element in the set of matched elements inside the
  278. // first element in the given Selection. The element is cloned before being
  279. // inserted into the document.
  280. //
  281. // It returns the original set of elements.
  282. func (s *Selection) WrapSelection(sel *Selection) *Selection {
  283. return s.wrapNodes(sel.Nodes...)
  284. }
  285. // WrapHtml wraps each element in the set of matched elements inside the inner-
  286. // most child of the given HTML.
  287. //
  288. // It returns the original set of elements.
  289. func (s *Selection) WrapHtml(html string) *Selection {
  290. return s.wrapNodes(parseHtml(html)...)
  291. }
  292. // WrapNode wraps each element in the set of matched elements inside the inner-
  293. // most child of the given node. The given node is copied before being inserted
  294. // into the document.
  295. //
  296. // It returns the original set of elements.
  297. func (s *Selection) WrapNode(n *html.Node) *Selection {
  298. return s.wrapNodes(n)
  299. }
  300. func (s *Selection) wrapNodes(ns ...*html.Node) *Selection {
  301. s.Each(func(i int, ss *Selection) {
  302. ss.wrapAllNodes(ns...)
  303. })
  304. return s
  305. }
  306. // WrapAll wraps a single HTML structure, matched by the given selector, around
  307. // all elements in the set of matched elements. The matched child is cloned
  308. // before being inserted into the document.
  309. //
  310. // It returns the original set of elements.
  311. func (s *Selection) WrapAll(selector string) *Selection {
  312. return s.WrapAllMatcher(compileMatcher(selector))
  313. }
  314. // WrapAllMatcher wraps a single HTML structure, matched by the given Matcher,
  315. // around all elements in the set of matched elements. The matched child is
  316. // cloned before being inserted into the document.
  317. //
  318. // It returns the original set of elements.
  319. func (s *Selection) WrapAllMatcher(m Matcher) *Selection {
  320. return s.wrapAllNodes(m.MatchAll(s.document.rootNode)...)
  321. }
  322. // WrapAllSelection wraps a single HTML structure, the first node of the given
  323. // Selection, around all elements in the set of matched elements. The matched
  324. // child is cloned before being inserted into the document.
  325. //
  326. // It returns the original set of elements.
  327. func (s *Selection) WrapAllSelection(sel *Selection) *Selection {
  328. return s.wrapAllNodes(sel.Nodes...)
  329. }
  330. // WrapAllHtml wraps the given HTML structure around all elements in the set of
  331. // matched elements. The matched child is cloned before being inserted into the
  332. // document.
  333. //
  334. // It returns the original set of elements.
  335. func (s *Selection) WrapAllHtml(html string) *Selection {
  336. return s.wrapAllNodes(parseHtml(html)...)
  337. }
  338. func (s *Selection) wrapAllNodes(ns ...*html.Node) *Selection {
  339. if len(ns) > 0 {
  340. return s.WrapAllNode(ns[0])
  341. }
  342. return s
  343. }
  344. // WrapAllNode wraps the given node around the first element in the Selection,
  345. // making all other nodes in the Selection children of the given node. The node
  346. // is cloned before being inserted into the document.
  347. //
  348. // It returns the original set of elements.
  349. func (s *Selection) WrapAllNode(n *html.Node) *Selection {
  350. if s.Size() == 0 {
  351. return s
  352. }
  353. wrap := cloneNode(n)
  354. first := s.Nodes[0]
  355. if first.Parent != nil {
  356. first.Parent.InsertBefore(wrap, first)
  357. first.Parent.RemoveChild(first)
  358. }
  359. for c := getFirstChildEl(wrap); c != nil; c = getFirstChildEl(wrap) {
  360. wrap = c
  361. }
  362. newSingleSelection(wrap, s.document).AppendSelection(s)
  363. return s
  364. }
  365. // WrapInner wraps an HTML structure, matched by the given selector, around the
  366. // content of element in the set of matched elements. The matched child is
  367. // cloned before being inserted into the document.
  368. //
  369. // It returns the original set of elements.
  370. func (s *Selection) WrapInner(selector string) *Selection {
  371. return s.WrapInnerMatcher(compileMatcher(selector))
  372. }
  373. // WrapInnerMatcher wraps an HTML structure, matched by the given selector,
  374. // around the content of element in the set of matched elements. The matched
  375. // child is cloned before being inserted into the document.
  376. //
  377. // It returns the original set of elements.
  378. func (s *Selection) WrapInnerMatcher(m Matcher) *Selection {
  379. return s.wrapInnerNodes(m.MatchAll(s.document.rootNode)...)
  380. }
  381. // WrapInnerSelection wraps an HTML structure, matched by the given selector,
  382. // around the content of element in the set of matched elements. The matched
  383. // child is cloned before being inserted into the document.
  384. //
  385. // It returns the original set of elements.
  386. func (s *Selection) WrapInnerSelection(sel *Selection) *Selection {
  387. return s.wrapInnerNodes(sel.Nodes...)
  388. }
  389. // WrapInnerHtml wraps an HTML structure, matched by the given selector, around
  390. // the content of element in the set of matched elements. The matched child is
  391. // cloned before being inserted into the document.
  392. //
  393. // It returns the original set of elements.
  394. func (s *Selection) WrapInnerHtml(html string) *Selection {
  395. return s.wrapInnerNodes(parseHtml(html)...)
  396. }
  397. // WrapInnerNode wraps an HTML structure, matched by the given selector, around
  398. // the content of element in the set of matched elements. The matched child is
  399. // cloned before being inserted into the document.
  400. //
  401. // It returns the original set of elements.
  402. func (s *Selection) WrapInnerNode(n *html.Node) *Selection {
  403. return s.wrapInnerNodes(n)
  404. }
  405. func (s *Selection) wrapInnerNodes(ns ...*html.Node) *Selection {
  406. if len(ns) == 0 {
  407. return s
  408. }
  409. s.Each(func(i int, s *Selection) {
  410. contents := s.Contents()
  411. if contents.Size() > 0 {
  412. contents.wrapAllNodes(ns...)
  413. } else {
  414. s.AppendNodes(cloneNode(ns[0]))
  415. }
  416. })
  417. return s
  418. }
  419. func parseHtml(h string) []*html.Node {
  420. // Errors are only returned when the io.Reader returns any error besides
  421. // EOF, but strings.Reader never will
  422. nodes, err := html.ParseFragment(strings.NewReader(h), &html.Node{Type: html.ElementNode})
  423. if err != nil {
  424. panic("goquery: failed to parse HTML: " + err.Error())
  425. }
  426. return nodes
  427. }
  428. func setHtmlNodes(s *Selection, ns ...*html.Node) *Selection {
  429. for _, n := range s.Nodes {
  430. for c := n.FirstChild; c != nil; c = n.FirstChild {
  431. n.RemoveChild(c)
  432. }
  433. for _, c := range ns {
  434. n.AppendChild(cloneNode(c))
  435. }
  436. }
  437. return s
  438. }
  439. // Get the first child that is an ElementNode
  440. func getFirstChildEl(n *html.Node) *html.Node {
  441. c := n.FirstChild
  442. for c != nil && c.Type != html.ElementNode {
  443. c = c.NextSibling
  444. }
  445. return c
  446. }
  447. // Deep copy a slice of nodes.
  448. func cloneNodes(ns []*html.Node) []*html.Node {
  449. cns := make([]*html.Node, 0, len(ns))
  450. for _, n := range ns {
  451. cns = append(cns, cloneNode(n))
  452. }
  453. return cns
  454. }
  455. // Deep copy a node. The new node has clones of all the original node's
  456. // children but none of its parents or siblings.
  457. func cloneNode(n *html.Node) *html.Node {
  458. nn := &html.Node{
  459. Type: n.Type,
  460. DataAtom: n.DataAtom,
  461. Data: n.Data,
  462. Attr: make([]html.Attribute, len(n.Attr)),
  463. }
  464. copy(nn.Attr, n.Attr)
  465. for c := n.FirstChild; c != nil; c = c.NextSibling {
  466. nn.AppendChild(cloneNode(c))
  467. }
  468. return nn
  469. }
  470. func (s *Selection) manipulateNodes(ns []*html.Node, reverse bool,
  471. f func(sn *html.Node, n *html.Node)) *Selection {
  472. lasti := s.Size() - 1
  473. // net.Html doesn't provide document fragments for insertion, so to get
  474. // things in the correct order with After() and Prepend(), the callback
  475. // needs to be called on the reverse of the nodes.
  476. if reverse {
  477. for i, j := 0, len(ns)-1; i < j; i, j = i+1, j-1 {
  478. ns[i], ns[j] = ns[j], ns[i]
  479. }
  480. }
  481. for i, sn := range s.Nodes {
  482. for _, n := range ns {
  483. if i != lasti {
  484. f(sn, cloneNode(n))
  485. } else {
  486. if n.Parent != nil {
  487. n.Parent.RemoveChild(n)
  488. }
  489. f(sn, n)
  490. }
  491. }
  492. }
  493. return s
  494. }