{"id":17297015,"url":"https://github.com/adamniederer/elquery","last_synced_at":"2026-01-06T22:15:37.220Z","repository":{"id":80548431,"uuid":"82442161","full_name":"AdamNiederer/elquery","owner":"AdamNiederer","description":"Read and manipulate HTML in emacs","archived":false,"fork":false,"pushed_at":"2022-03-31T01:43:35.000Z","size":79,"stargazers_count":43,"open_issues_count":3,"forks_count":5,"subscribers_count":6,"default_branch":"master","last_synced_at":"2025-02-01T02:41:22.630Z","etag":null,"topics":["emacs-lisp","html","selectors","web-scale"],"latest_commit_sha":null,"homepage":null,"language":"Emacs Lisp","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/AdamNiederer.png","metadata":{"files":{"readme":"README.org","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-02-19T07:15:41.000Z","updated_at":"2024-12-03T12:12:56.000Z","dependencies_parsed_at":null,"dependency_job_id":"24af6ba6-b44c-45e1-b8c9-4755ac136f5f","html_url":"https://github.com/AdamNiederer/elquery","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AdamNiederer%2Felquery","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AdamNiederer%2Felquery/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AdamNiederer%2Felquery/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AdamNiederer%2Felquery/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/AdamNiederer","download_url":"https://codeload.github.com/AdamNiederer/elquery/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245733999,"owners_count":20663620,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["emacs-lisp","html","selectors","web-scale"],"created_at":"2024-10-15T11:14:27.765Z","updated_at":"2026-01-06T22:15:37.180Z","avatar_url":"https://github.com/AdamNiederer.png","language":"Emacs Lisp","funding_links":[],"categories":[],"sub_categories":[],"readme":"#+TITLE: elquery: parse, query, and format HTML with Emacs Lisp\n#+AUTHOR: Adam Niederer\n\n#+BEGIN_HTML\n\u003cdiv\u003e\n  \u003ca href=\"https://www.gnu.org/licenses/gpl-3.0.en.html\"\u003e\n    \u003cimg src=\"https://img.shields.io/badge/license-GPLv3-brightgreen.svg\"/\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://melpa.org/#/elquery\"\u003e\n    \u003cimg src=\"https://melpa.org/packages/elquery-badge.svg\"/\u003e\n  \u003c/a\u003e\n  \u003ca style=\"filter: invert(100%);\" href=\"https://codecov.io/gh/AdamNiederer/elquery\"\u003e\n    \u003cimg src=\"https://codecov.io/gh/AdamNiederer/elquery/branch/master/graph/badge.svg\"/\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://travis-ci.org/AdamNiederer/elquery\"\u003e\n    \u003cimg src=\"https://api.travis-ci.org/AdamNiederer/elquery.svg?branch=master\"/\u003e\n  \u003c/a\u003e\n\u003c/div\u003e\n#+END_HTML\n\n#+BEGIN_QUOTE\nWrite things. Do things.\n#+END_QUOTE\n\nelquery is a library that lets you parse, query, set, and format HTML using\nEmacs Lisp. It implements (most of) the [[https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector][querySelector]] API with ~elquery-$~, and\ncan get and set HTML attributes.\n\n* Usage\nGiven the below HTML file, some usage examples include:\n#+BEGIN_SRC html\n  \u003chtml style=\"height: 100vh\"\u003e\n    \u003chead class=\"baz\"\u003e\u003ctitle class=\"baz\" data-bar=\"foo\"\u003eComplex HTML Page\u003c/title\u003e\u003c/head\u003e\n    \u003cbody class=\"baz bur\" style=\"height: 100%\"\u003e\n      \u003ch1 id=\"bar\" class=\"baz wow\"\u003eWow this is an \u003cem\u003eexample\u003c/em\u003e\u003c/h1\u003e\n      \u003cinput id=\"quux\" class=\"baz foo\"/\u003e\n      \u003ciframe sandbox=\"allow-same-origin allow-scripts allow-popups allow-forms\"\n              width=\"100%\" height=\"100%\" src=\"example.org\"\u003e\n      \u003c/iframe\u003e\n    \u003c/body\u003e\n  \u003c/html\u003e\n#+END_SRC\n#+BEGIN_SRC elisp\n  (let ((html (elquery-read-file \"~/baz.html\")))\n    ;;; Query elements\n    (elquery-el (car (elquery-$ \".baz#quux\" html)))\n    ;; =\u003e \"input\"\n    (mapcar 'elquery-el (elquery-$ \".baz\" html))\n    ;; =\u003e (\"input\" \"h1\" \"body\" \"title\" \"head\")\n    (mapcar (lambda (el) (elquery-el (elquery-parent el))) (elquery-$ \".baz\" html))\n    ;; =\u003e (\"body\" \"body\" \"html\" \"head\" \"html\")\n    (mapcar (lambda (el) (mapcar 'elquery-el (elquery-siblings el))) (elquery-$ \".baz\" html))\n    ;; =\u003e ((\"h1\" \"input\" \"iframe\") (\"h1\" \"input\" \"iframe\") (\"head\" \"body\") (\"title\") (\"head\" \"body\"))\n\n    ;;; Read properties of elements\n    (elquery-classes (car (elquery-$ \".baz#quux\" html)))\n    ;; =\u003e (\"baz\" \"foo\")\n    (elquery-prop (car (elquery-$ \"iframe\" html)) \"sandbox\")\n    ;; =\u003e \"allow-same-origin allow-scripts allow-popups allow-forms\"\n    (elquery-text (car (elquery-$ \"h1\" html)))\n    ;; =\u003e \"Wow this is an\"\n    (elquery-full-text (car (elquery-$ \"h1\" html)) \" \")\n    ;; =\u003e \"Wow this is an example\"\n\n    ;;; Write parsed HTML\n    (elquery-write html nil)\n    ;; =\u003e \"\u003chtml style=\\\"height: 100vh\\\"\u003e ... \u003c/html\u003e\"\n    )\n#+END_SRC\n* Functions\n** I/O and Parsing\n- ~(elquery-read-url URL)~ - Return a parsed tree of the HTML at URL\n- ~(elquery-read-file FILE)~ - Return a parsed tree of the HTML in FILE\n- ~(elquery-read-buffer BUFFER)~ - Return a parsed tree of the HTML in BUFFER\n- ~(elquery-read-string STRING)~ - Return a parsed tree of the HTML in STRING\n- ~(elquery-write TREE)~ - Return a string of the HTML representation of TREE\n  and its children.\n- ~(elquery-fmt TREE)~ - Return a string of the HTML representation of the\n  topmost node of TREE, without its children or closing tag.\n- ~(elquery-pprint TREE)~ - Return a pretty, CSS-selector-like representation of\n  the topmost node of TREE.\n** DOM Traversal\n- ~(elquery-$ SELECTOR TREE)~ - Return a list of subtrees of TREE which matches\n  the query string. Currently, the element name, id, class, and property values\n  are supported, along with some set operations and hierarchy relationships. See\n  Selector Syntax for a more detailed overview of valid selectors.\n- ~(elquery-parent NODE)~ - Return the parent of NODE\n- ~(elquery-child NODE)~ - Return the first child of NODE\n- ~(elquery-children NODE)~ - Return a list of the children of NODE\n- ~(elquery-siblings NODE)~ - Return a list of the siblings of NODE\n- ~(elquery-next-children NODE)~ - Return a list of children of NODE with the\n  children's children removed.\n- ~(elquery-next-sibling NODE)~ - Return the sibling immediately following NODE\n** Common Trait Selection\n- ~(elquery-class? NODE CLASS)~ - Return whether NODE has the class CLASS\n- ~(elquery-id NODE)~ - Return the id of NODE\n- ~(elquery-classes NODE)~ - Return a list of the classes of NODE\n- ~(elquery-text NODE)~ - Return the text content of NODE. If there are multiple\n  text elements, for example, ~\u003cspan\u003eHello \u003cspan\u003ecruel\u003c/span\u003e world\u003c/span\u003e~,\n  return the concatenation of these nodes, ~Hello  world~, with two spaces\n  between ~Hello~ and ~world~\n- ~(elquery-full-text NODE)~ - Return the text content of NODE and its\n  children. If there are multiple text elements, for example, ~\u003cspan\u003eHello\n  \u003cspan\u003ecruel\u003c/span\u003e world\u003c/span\u003e~, return the concatenation of these nodes,\n  ~Hello cruel world~.\n** Property Modification\n- ~(elquery-props NODE)~ - Return a plist of this node's properties, including\n  its id, class, and data attributes.\n- ~(elquery-data NODE KEY \u0026optional VAL)~ - Return the value of NODE's data-KEY\n  property. If VAL is supplied, destructively set NODE's data-KEY property to\n  VAL. For example, on the node ~\u003cspan data-name=\"adam\"\u003e~, ~(elquery-data node\n  \"name\")~ would return ~adam~\n- ~(elquery-prop NODE PROP \u0026optional VAL)~ - Return the value of PROP in\n  NODE. If VAL is supplied, destructively set PROP to VAL.\n- ~(elquery-rm-prop NODE)~ - Destructively remove PROP from NODE\n** Predicates\n- ~(elquery-nodep OBJ)~ - Return whether OBJ is a DOM node\n- ~(elquery-elp OBJ)~ - Return whether OBJ is not a text node\n- ~(elquery-textp OBJ)~ - Return whether OBJ is a text node\n** General Tree Functions\nBecause HTML is a large tree representation, elq includes some general tree\nmanipulation functions which it uses internally, and may be useful to you when\ndealing with the DOM.\n\n- ~(elquery-tree-remove-if pred tree)~ - Remove all elements from TREE if they\n  satisfy PRED. Preserves the structure and order of the tree.\n- ~(elquery-tree-remove-if-not pred tree)~ - Remove all elements from TREE if\n  they do not satisfy PRED. Preserves the structure and order of the tree.\n- ~(elquery-tree-mapcar fn tree)~ - Apply FN to all elements in TREE\n- ~(elquery-tree-reduce fn tree)~ - Perform an in-order reduction of TREE with\n  FN.  Equivalent to a reduction on a flattened tree.\n- ~(elquery-tree-flatten tree)~ - Flatten the tree, removing all list nesting\n  and leaving a list of only atomic elements. This does not preserve the order\n  of the elements.\n- ~(elquery-tree-flatten-until pred tree)~ - Flatten the tree, but treat\n  elements matching PRED as atomic elements, not preserving order.\n* Selector Syntax\nWe support a significant subset of jQuery's selector syntax. If I ever decide to\nmake this project even more web-scale, I'll add colon selectors and more\nproperty equality tests.\n\n- ~#foo~ - Select all elements with the id \"foo\"\n- ~.bar~ - Select all elements with the class \"bar\"\n- ~[name=user]~ - Select all elements whose \"name\" property is \"user\"\n- ~#foo.bar[name=user]~ - Logical intersection of the above three selectors.\n  Select all elements whose id is \"foo\", class is \".bar\", and \"name\" is \"user\"\n- ~#foo .bar, [name=user]~ - Select all elements with the class \"bar\" in the\n  subtrees of all elements with the id \"foo\", along with all elements whose\n  \"name\" is \"user\"\n- ~#foo \u003e .bar~ - Select all elements with class \"bar\" whose immediate parent\n  has id \"foo\"\n- ~#foo ~ .bar~ - Select all elements with class \"bar\" which are siblings of\n  elements with id \"foo\"\n- ~#foo + .bar~ - Select all elements with class \"bar\" which immediately follow\n  elements with id \"foo\"\n\nAll permutations of union, intersection, child, next-child, and sibling\nrelationships are supported.\n* Internal Data Structure\nEach element is a plist, which is guaranteed to have at least one key-value\npair, and an ~:el~ key. All elements of this plist are accessible with the above\nfunctions, but the internal representation of a document node is below for\nanybody brave enough to hack on this:\n\n- ~:el~ - A string containing the name of the element. If the node is a \"text\n  node\", ~:el is nil~\n- ~:text~ - A string containing the concatenation of all text elements\n  immediately below this one on the tree. For example, the node representing\n  ~\u003cspan\u003eHello \u003cspan\u003ecruel\u003c/span\u003e world\u003c/span\u003e~ would be ~Hello world\".\n- ~:props~ - A plist of HTML properties for each element, including but not\n  limited to its ~:id~, ~class~, ~data-*~, and ~name~ attributes.\n- ~:parent~ - A pointer to the parent element. Emacs thinks this is a list.\n- ~:children~ - A list of elements immediately below this one on the tree,\n  including text nodes.\n\nThe data structure used in queries via ~(elquery-$)~ is very similar, although\nit doesn't have ~:text~ keyword (PRs welcome!) and has an extra ~:rel~ keyword,\nwhich specifies the relationship between the query and its ~:children~. ~:rel~\nmay be one of ~:next-child~, ~:child~, ~next-sibling~, and ~:sibling~. This is\nused by the internal function ~(elquery--$)~ which must determine whether it can\ncontinue recursion down the tree based on the relationship of two intersections\nin a selector.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fadamniederer%2Felquery","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fadamniederer%2Felquery","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fadamniederer%2Felquery/lists"}