{"id":1747,"url":"https://github.com/iabudiab/HTMLKit","last_synced_at":"2025-08-02T04:32:31.407Z","repository":{"id":56913946,"uuid":"48508179","full_name":"iabudiab/HTMLKit","owner":"iabudiab","description":"An Objective-C framework for your everyday HTML needs.","archived":false,"fork":false,"pushed_at":"2023-07-07T02:17:01.000Z","size":2572,"stargazers_count":241,"open_issues_count":6,"forks_count":26,"subscribers_count":12,"default_branch":"main","last_synced_at":"2025-06-17T06:35:58.203Z","etag":null,"topics":["css3-selectors","dom","html","html-dom-parser","ios","macos","objective-c","parsing","swift","tvos","watchos","whatwg"],"latest_commit_sha":null,"homepage":null,"language":"HTML","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/iabudiab.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","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}},"created_at":"2015-12-23T19:44:47.000Z","updated_at":"2025-04-10T11:49:42.000Z","dependencies_parsed_at":"2024-01-07T22:23:31.915Z","dependency_job_id":"16af269a-1c49-4a8d-92b1-cda63734b756","html_url":"https://github.com/iabudiab/HTMLKit","commit_stats":{"total_commits":821,"total_committers":7,"mean_commits":"117.28571428571429","dds":"0.040194884287454324","last_synced_commit":"118cb4197164cc6987f41fae7e1eb21eb96b5f20"},"previous_names":[],"tags_count":29,"template":false,"template_full_name":null,"purl":"pkg:github/iabudiab/HTMLKit","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iabudiab%2FHTMLKit","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iabudiab%2FHTMLKit/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iabudiab%2FHTMLKit/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iabudiab%2FHTMLKit/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/iabudiab","download_url":"https://codeload.github.com/iabudiab/HTMLKit/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iabudiab%2FHTMLKit/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":268334618,"owners_count":24233793,"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","status":"online","status_checked_at":"2025-08-02T02:00:12.353Z","response_time":74,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["css3-selectors","dom","html","html-dom-parser","ios","macos","objective-c","parsing","swift","tvos","watchos","whatwg"],"created_at":"2024-01-05T20:15:54.827Z","updated_at":"2025-08-02T04:32:31.047Z","avatar_url":"https://github.com/iabudiab.png","language":"HTML","readme":"# HTMLKit\n\n![HTMLKit Logo](https://raw.githubusercontent.com/iabudiab/HTMLKit/main/HTMLKit.png)\n\nAn Objective-C framework for your everyday HTML needs.\n\n[![HTMLKit CI](https://github.com/iabudiab/HTMLKit/actions/workflows/ci.yml/badge.svg)](https://github.com/iabudiab/HTMLKit/actions/workflows/ci.yml)\n[![codecov](https://codecov.io/gh/iabudiab/HTMLKit/branch/main/graph/badge.svg)](https://codecov.io/gh/iabudiab/HTMLKit)\n[![Carthage Compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage)\n[![CocoaPods Compatible](https://img.shields.io/cocoapods/v/HTMLKit.svg?style=flat)](https://cocoapods.org/pods/HTMLKit)\n[![Platform](https://img.shields.io/cocoapods/p/HTMLKit.svg?style=flat)](http://cocoadocs.org/docsets/HTMLKit)\n[![License MIT](https://img.shields.io/badge/license-MIT-4481C7.svg?style=flat)](https://opensource.org/licenses/MIT)\n\n- [Quick Overview](#overview)\n- [Installation](#installation)\n- [Parsing](#parsing)\n- [The DOM](#the-dom)\n- [CSS3 Selectors](#css3-selectors)\n\n# Quick Overview\n\nHTMLKit is a [WHATWG specification](https://html.spec.whatwg.org/multipage/)-compliant framework for parsing and serializing HTML documents and document fragments for iOS and OSX. HTMLKit parses real-world HTML the same way modern web browsers would.\n\nHTMLKit provides a rich DOM implementation for manipulating and navigating the document tree. It also understands [CSS3 selectors](http://www.w3.org/TR/css3-selectors/) making node-selection and querying the DOM a piece of cake.\n\n## DOM Validation\n\nDOM mutations are validated as described in the [WHATWG DOM Standard](https://dom.spec.whatwg.org). Invalid DOM manipulations throw hierarchy-related exceptions. You can disable these validations, which will also increase the performance by about 20-30%, by defining the `HTMLKIT_NO_DOM_CHECKS` compiler constant.\n\n## Tests\n\nHTMLKit passes all of the [HTML5Lib](https://github.com/html5lib/html5lib-tests) Tokenizer and Tree Construction tests. The `html5lib-tests` is configured as a git-submodule. If you plan to run the tests, do not forget to pull it too.\n\nThe CSS3 Selector implementation is tested with an adapted version of the [CSS3 Selectors Test Suite](http://www.w3.org/Style/CSS/Test/CSS3/Selectors/current/html/full/flat/index.html), ignoring the tests that require user interaction, session history, and scripting.\n\n## Does it Swift?\n\nCheck out the playground!\n\n# Installation\n\n## Carthage\n\n[Carthage](https://github.com/Carthage/Carthage) is a decentralized dependency manager that builds your dependencies and provides you with binary frameworks.\n\nIf you don't have Carthage yet, you can install it with Homebrew using the following command:\n\n```bash\n$ brew update\n$ brew install carthage\n```\n\nTo add `HTMLKit` as a dependency into your project using Carthage just add the following line in your `Cartfile`:\n\n```\ngithub \"iabudiab/HTMLKit\"\n```\n\nThen run the following command to build the framework and drag the built `HTMLKit.framework` into your Xcode project.\n\n```bash\n$ carthage update\n```\n\n## CocoaPods\n\n[CocoaPods](http://cocoapods.org) is a dependency manager for Cocoa projects.\n\nIf you don't have CocoaPods yet, you can install it with the following command:\n\n```bash\n$ gem install cocoapods\n```\n\nTo add `HTMLKit` as a dependency into your project using CocoaPods just add the following in your `Podfile`:\n\n```ruby\ntarget 'MyTarget' do\n  pod 'HTMLKit', '~\u003e 4.2'\nend\n```\n\nThen, run the following command:\n\n```bash\n$ pod install\n```\n\n## Swift Package Manager\n\n[Swift Package Manager](https://github.com/apple/swift-package-manager) is the package manager for the Swift programming language.\n\nAdd `HTMLKit` to your `Package.swift` dependecies:\n\n```swift\n.package(url: \"https://github.com/iabudiab/HTMLKit\", .upToNextMajor(from: \"4.0.0\")),\n```\n\nThen run:\n\n```bash\n$ swift build\n```\n\n## Manually\n\n1- Add `HTMLKit` as git submodule\n\n```bash\n$ git submodule add https://github.com/iabudiab/HTMLKit.git\n```\n\n2- Open the `HTMLKit` folder and drag'n'drop the `HTMLKit.xcodeproj` into the Project Navigator in Xcode to add it as a sub-project.\n\n3- In the General panel of your target add `HTMLKit.framework` under the `Embedded Binaries` \n\n# Parsing\n\n## Parsing Documents\n\nGiven some HTML content, you can parse it either via the `HTMLParser` or instatiate a `HTMLDocument` directly:\n\n```objective-c\nNSString *htmlString = @\"\u003cdiv\u003e\u003ch1\u003eHTMLKit\u003c/h1\u003e\u003cp\u003eHello there!\u003c/p\u003e\u003c/div\u003e\";\n\n// Via parser\nHTMLParser *parser = [[HTMLParser alloc] initWithString:htmlString];\nHTMLDocument *document = [parser parseDocument];\n\n// Via static initializer\nHTMLDocument *document = [HTMLDocument documentWithString:htmlString];\n```\n\n## Parsing Fragments\n\nYou can also prase HTML content as a document fragment with a specified context element:\n\n```objective-c\nNSString *htmlString = @\"\u003cdiv\u003e\u003ch1\u003eHTMLKit\u003c/h1\u003e\u003cp\u003eHello there!\u003c/p\u003e\u003c/div\u003e\";\n\nHTMLParser *parser = [[HTMLParser alloc] initWithString: htmlString];\n\nHTMLElement *tableContext = [[HTMLElement alloc] initWithTagName:@\"table\"];\nNSArray *nodes = [parser parseFragmentWithContextElement:tableContext];\n\nfor (HTMLNode *node in nodes) {\n\tNSLog(@\"%@\", node.outerHTML);\n}\n\n// The same parser instance can be reusued:\nHTMLElement *bodyContext = [[HTMLElement alloc] initWithTagName:@\"body\"];\nnodes = [parser parseFragmentWithContextElement:bodyContext];\n```\n\n# The DOM\n\nThe DOM tree can be manipulated in several ways, here are just a few:\n\n* Create new elements and assign attributes\n\n```objective-c\nHTMLElement *description = [[HTMLElement alloc] initWithTagName:@\"meta\"  attributes: @{@\"name\": @\"description\"}];\ndescription[@\"content\"] = @\"HTMLKit for iOS \u0026 OSX\";\n```\n\n* Append nodes to the document\n\n```objective-c\nHTMLElement *head = document.head;\n[head appendNode:description];\n\nHTMLElement *body = document.body;\nNSArray *nodes = @[\n\t[[HTMLElement alloc] initWithTagName:@\"div\" attributes: @{@\"class\": @\"red\"}],\n\t[[HTMLElement alloc] initWithTagName:@\"div\" attributes: @{@\"class\": @\"green\"}],\n\t[[HTMLElement alloc] initWithTagName:@\"div\" attributes: @{@\"class\": @\"blue\"}]\n];\n[body appendNodes:nodes];\n```\n\n* Enumerate child elements and perform DOM editing\n\n```objective-c\n[body enumerateChildElementsUsingBlock:^(HTMLElement *element, NSUInteger idx, BOOL *stop) {\n\tif ([element.tagName isEqualToString:@\"div\"]) {\n\t\tHTMLElement *lorem = [[HTMLElement alloc] initWithTagName:@\"p\"];\n\t\tlorem.textContent = [NSString stringWithFormat:@\"Lorem ipsum: %lu\", (unsigned long)idx];\n\t\t[element appendNode:lorem];\n\t}\n}];\n```\n\n* Remove nodes from the document\n\n```objective-c\n[body removeChildNodeAtIndex:1];\n[head removeAllChildNodes];\n[body.lastChild removeFromParentNode];\n```\n\n* Manipulate the HTML directly\n\n```objective-c\ngreenDiv.innerHTML = @\"\u003cul\u003e\u003cli\u003eitem 1\u003cli\u003eitem 2\";\n```\n\n* Navigate to child and sibling nodes\n\n```objective-c\nHTMLNode *firstChild = body.firstChild;\nHTMLNode *greenDiv = firstChild.nextSibling;\n```\n\n* Iterate the DOM tree with custom filters\n\n```objective-c\nHTMLNodeFilterBlock *filter =[HTMLNodeFilterBlock filterWithBlock:^ HTMLNodeFilterValue (HTMLNode *node) {\n\tif (node.childNodesCount != 1) {\n\t\treturn HTMLNodeFilterReject;\n\t}\n\treturn HTMLNodeFilterAccept;\n}];\n\nfor (HTMLElement *element in [body nodeIteratorWithShowOptions:HTMLNodeFilterShowElement filter:filter]) {\n\tNSLog(@\"%@\", element.outerHTML);\n}\n```\n\n* Create and manipulate DOM Ranges\n\n```objective-c\nHTMLDocument *document = [HTMLDocument documentWithString:@\"\u003cdiv\u003e\u003ch1\u003eHTMLKit\u003c/h1\u003e\u003cp id='foo'\u003eHello there!\u003c/p\u003e\u003c/div\u003e\"];\nHTMLRange *range = [[HTMLRange alloc] initWithDocument:document];\n\nHTMLNode *paragraph = [document querySelector:@\"#foo\"];\n[range selectNode:paragraph];\n[range extractContents];\n```\n\n# CSS3 Selectors\n\nAll CSS3 Selectors are supported except for the pseudo-elements (`::first-line`, `::first-letter`, ...etc.). You can use them the way you always have:\n\n```objective-c\n// Given the document:\nNSString *htmlString = @\"\u003cdiv\u003e\u003ch1\u003eHTMLKit\u003c/h1\u003e\u003cp class='greeting'\u003eHello there!\u003c/p\u003e\u003cp class='description'\u003eThis is a demo of HTMLKit\u003c/p\u003e\u003c/div\u003e\";\nHTMLDocument *document = [HTMLDocument documentWithString: htmlString];\n\n// Here are some of the supported selectors\nNSArray *paragraphs = [document querySelectorAll:@\"p\"];\nNSArray *paragraphsOrHeaders = [document querySelectorAll:@\"p, h1\"];\nNSArray *hasClassAttribute = [document querySelectorAll:@\"[class]\"];\nNSArray *greetings = [document querySelectorAll:@\".greeting\"];\nNSArray *classNameStartsWith_de = [document querySelectorAll:@\"[class^='de']\"];\n\nNSArray *hasAdjacentHeader = [document querySelectorAll:@\"h1 + *\"];\nNSArray *hasSiblingHeader = [document querySelectorAll:@\"h1 ~ *\"];\nNSArray *hasSiblingParagraph = [document querySelectorAll:@\"p ~ *\"];\n\nNSArray *nonParagraphChildOfDiv = [document querySelectorAll:@\"div :not(p)\"];\n```\n\nHTMLKit also provides API to create selector instances in a type-safe manner without the need to parse them first. The previous examples would like this:\n\n```objective-c\nNSArray *paragraphs = [document elementsMatchingSelector:typeSelector(@\"p\")];\nNSArray *paragraphsOrHeaders = [document elementsMatchingSelector:\n\tanyOf(@[\n\t\ttypeSelector(@\"p\"), typeSelector(@\"h1\")\n\t])\n];\n\nNSArray *hasClassAttribute = [document elementsMatchingSelector:hasAttributeSelector(@\"class\")];\nNSArray *greetings = [document elementsMatchingSelector:classSelector(@\"greeting\")];\nNSArray *classNameStartsWith_de = [document elementsMatchingSelector:attributeSelector(CSSAttributeSelectorBegins, @\"class\", @\"de\")];\n\nNSArray *hasAdjacentHeader = [document elementsMatchingSelector:adjacentSiblingSelector(typeSelector(@\"h1\"))];\nNSArray *hasSiblingHeader = [document elementsMatchingSelector:generalSiblingSelector(typeSelector(@\"h1\"))];\nNSArray *hasSiblingParagraph = [document elementsMatchingSelector:generalSiblingSelector(typeSelector(@\"p\"))];\n\nNSArray *nonParagraphChildOfDiv = [document elementsMatchingSelector:\n\tallOf(@[\n\t\tchildOfElementSelector(typeSelector(@\"div\")),\n\t\tnot(typeSelector(@\"p\"))\n\t])\n];\n```\n\nHere are more examples:\n\n```objective-c\nHTMLNode *firstDivElement = [document firstElementMatchingSelector:typeSelector(@\"div\")];\n\nNSArray *secondChildOfDiv = [firstDivElement querySelectorAll:@\":nth-child(2)\"];\nNSArray *secondOfType = [firstDivElement querySelectorAll:@\":nth-of-type(2n)\"];\n\nsecondChildOfDiv = [firstDivElement elementsMatchingSelector:nthChildSelector(CSSNthExpressionMake(0, 2))];\nsecondOfType = [firstDivElement elementsMatchingSelector:nthOfTypeSelector(CSSNthExpressionMake(2, 0))];\n\nNSArray *notParagraphAndNotDiv = [firstDivElement querySelectorAll:@\":not(p):not(div)\"];\nnotParagraphAndNotDiv = [firstDivElement elementsMatchingSelector:\n\tallOf([\n\t\tnot(typeSelector(@\"p\")),\n\t\tnot(typeSelector(@\"div\"))\n\t])\n];\n```\n\nOne more thing! You can also create your own selectors. You either subclass the CSSSelector or just use the block-based wrapper. For example the previous selector can be implemented like this:\n\n```objective-c\nCSSSelector *myAwesomeSelector = namedBlockSelector(@\"myAwesomeSelector\", ^BOOL (HTMLElement *element) {\n\treturn ![element.tagName isEqualToString:@\"p\"] \u0026\u0026 ![element.tagName isEqualToString:@\"div\"];\n});\nnotParagraphAndNotDiv = [firstDivElement elementsMatchingSelector:myAwesomeSelector];\n```\n\n# Change Log\n\nSee the [CHANGELOG.md](CHANGELOG.md) for more info.\n\n# License\n\nHTMLKit is available under the MIT license. See the [LICENSE](LICENSE) file for more info.\n","funding_links":[],"categories":["Parsing"],"sub_categories":["XML \u0026 HTML","Other free courses"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fiabudiab%2FHTMLKit","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fiabudiab%2FHTMLKit","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fiabudiab%2FHTMLKit/lists"}