Repository files navigation

HTMLKit

HTMLKit Logo

An Objective-C framework for your everyday HTML needs.

HTMLKit CIcodecovCarthage CompatibleCocoaPods CompatiblePlatformLicense MIT

Quick Overview

HTMLKit is a WHATWG specification-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.

HTMLKit provides a rich DOM implementation for manipulating and navigating the document tree. It also understands CSS3 selectors making node-selection and querying the DOM a piece of cake.

DOM Validation

DOM mutations are validated as described in the WHATWG DOM Standard. 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.

Tests

HTMLKit passes all of the HTML5Lib 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.

The CSS3 Selector implementation is tested with an adapted version of the CSS3 Selectors Test Suite, ignoring the tests that require user interaction, session history, and scripting.

Does it Swift?

Check out the playground!

Installation

Carthage

Carthage is a decentralized dependency manager that builds your dependencies and provides you with binary frameworks.

If you don't have Carthage yet, you can install it with Homebrew using the following command:

$ brew update
$ brew install carthage

To add HTMLKit as a dependency into your project using Carthage just add the following line in your Cartfile:

github "iabudiab/HTMLKit"

Then run the following command to build the framework and drag the built HTMLKit.framework into your Xcode project.

$ carthage update

CocoaPods

CocoaPods is a dependency manager for Cocoa projects.

If you don't have CocoaPods yet, you can install it with the following command:

$ gem install cocoapods

To add HTMLKit as a dependency into your project using CocoaPods just add the following in your Podfile:

target'MyTarget'dopod'HTMLKit','~> 4.2'end

Then, run the following command:

$ pod install

Swift Package Manager

Swift Package Manager is the package manager for the Swift programming language.

Add HTMLKit to your Package.swift dependecies:

.package(url:"https://github.com/iabudiab/HTMLKit",.upToNextMajor(from:"4.0.0")),

Then run:

$ swift build

Manually

1- Add HTMLKit as git submodule

$ git submodule add https://github.com/iabudiab/HTMLKit.git

2- Open the HTMLKit folder and drag'n'drop the HTMLKit.xcodeproj into the Project Navigator in Xcode to add it as a sub-project.

3- In the General panel of your target add HTMLKit.framework under the Embedded Binaries

Parsing

Parsing Documents

Given some HTML content, you can parse it either via the HTMLParser or instatiate a HTMLDocument directly:

NSString *htmlString = @"<div><h1>HTMLKit</h1><p>Hello there!</p></div>";
// Via parser
HTMLParser *parser = [[HTMLParser alloc] initWithString:htmlString];
HTMLDocument *document = [parser parseDocument];
// Via static initializer
HTMLDocument *document = [HTMLDocument documentWithString:htmlString];

Parsing Fragments

You can also prase HTML content as a document fragment with a specified context element:

NSString *htmlString = @"<div><h1>HTMLKit</h1><p>Hello there!</p></div>";
HTMLParser *parser = [[HTMLParser alloc] initWithString: htmlString];
HTMLElement *tableContext = [[HTMLElement alloc] initWithTagName:@"table"];
NSArray *nodes = [parser parseFragmentWithContextElement:tableContext];
for (HTMLNode *node in nodes) {
NSLog(@"%@", node.outerHTML);
}
// The same parser instance can be reusued:
HTMLElement *bodyContext = [[HTMLElement alloc] initWithTagName:@"body"];
nodes = [parser parseFragmentWithContextElement:bodyContext];

The DOM

The DOM tree can be manipulated in several ways, here are just a few:

  • Create new elements and assign attributes
HTMLElement *description = [[HTMLElement alloc] initWithTagName:@"meta"attributes: @{@"name": @"description"}];
description[@"content"] = @"HTMLKit for iOS & OSX";
  • Append nodes to the document
HTMLElement *head = document.head;
[head appendNode:description];
HTMLElement *body = document.body;
NSArray *nodes = @[
[[HTMLElement alloc] initWithTagName:@"div"attributes: @{@"class": @"red"}],
[[HTMLElement alloc] initWithTagName:@"div"attributes: @{@"class": @"green"}],
[[HTMLElement alloc] initWithTagName:@"div"attributes: @{@"class": @"blue"}]
];
[body appendNodes:nodes];
  • Enumerate child elements and perform DOM editing
[body enumerateChildElementsUsingBlock:^(HTMLElement *element, NSUInteger idx, BOOL *stop) {
if ([element.tagName isEqualToString:@"div"]) {
HTMLElement *lorem = [[HTMLElement alloc] initWithTagName:@"p"];
lorem.textContent = [NSStringstringWithFormat:@"Lorem ipsum: %lu", (unsignedlong)idx];
[element appendNode:lorem];
}
}];
  • Remove nodes from the document
[body removeChildNodeAtIndex:1];
[head removeAllChildNodes];
[body.lastChild removeFromParentNode];
  • Manipulate the HTML directly
greenDiv.innerHTML = @"<ul><li>item 1<li>item 2";
  • Navigate to child and sibling nodes
HTMLNode *firstChild = body.firstChild;
HTMLNode *greenDiv = firstChild.nextSibling;
  • Iterate the DOM tree with custom filters
HTMLNodeFilterBlock *filter =[HTMLNodeFilterBlock filterWithBlock:^ HTMLNodeFilterValue (HTMLNode *node) {
if (node.childNodesCount != 1) {
return HTMLNodeFilterReject;
}
return HTMLNodeFilterAccept;
}];
for (HTMLElement *element in [body nodeIteratorWithShowOptions:HTMLNodeFilterShowElement filter:filter]) {
NSLog(@"%@", element.outerHTML);
}
  • Create and manipulate DOM Ranges
HTMLDocument *document = [HTMLDocument documentWithString:@"<div><h1>HTMLKit</h1><p id='foo'>Hello there!</p></div>"];
HTMLRange *range = [[HTMLRange alloc] initWithDocument:document];
HTMLNode *paragraph = [document querySelector:@"#foo"];
[range selectNode:paragraph];
[range extractContents];

CSS3 Selectors

All CSS3 Selectors are supported except for the pseudo-elements (::first-line, ::first-letter, ...etc.). You can use them the way you always have:

// Given the document:NSString *htmlString = @"<div><h1>HTMLKit</h1><p class='greeting'>Hello there!</p><p class='description'>This is a demo of HTMLKit</p></div>";
HTMLDocument *document = [HTMLDocument documentWithString: htmlString];
// Here are some of the supported selectorsNSArray *paragraphs = [document querySelectorAll:@"p"];
NSArray *paragraphsOrHeaders = [document querySelectorAll:@"p, h1"];
NSArray *hasClassAttribute = [document querySelectorAll:@"[class]"];
NSArray *greetings = [document querySelectorAll:@".greeting"];
NSArray *classNameStartsWith_de = [document querySelectorAll:@"[class^='de']"];
NSArray *hasAdjacentHeader = [document querySelectorAll:@"h1 + *"];
NSArray *hasSiblingHeader = [document querySelectorAll:@"h1 ~ *"];
NSArray *hasSiblingParagraph = [document querySelectorAll:@"p ~ *"];
NSArray *nonParagraphChildOfDiv = [document querySelectorAll:@"div :not(p)"];

HTMLKit 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:

NSArray *paragraphs = [document elementsMatchingSelector:typeSelector(@"p")];
NSArray *paragraphsOrHeaders = [document elementsMatchingSelector:anyOf(@[
typeSelector(@"p"), typeSelector(@"h1")
])
];
NSArray *hasClassAttribute = [document elementsMatchingSelector:hasAttributeSelector(@"class")];
NSArray *greetings = [document elementsMatchingSelector:classSelector(@"greeting")];
NSArray *classNameStartsWith_de = [document elementsMatchingSelector:attributeSelector(CSSAttributeSelectorBegins, @"class", @"de")];
NSArray *hasAdjacentHeader = [document elementsMatchingSelector:adjacentSiblingSelector(typeSelector(@"h1"))];
NSArray *hasSiblingHeader = [document elementsMatchingSelector:generalSiblingSelector(typeSelector(@"h1"))];
NSArray *hasSiblingParagraph = [document elementsMatchingSelector:generalSiblingSelector(typeSelector(@"p"))];
NSArray *nonParagraphChildOfDiv = [document elementsMatchingSelector:allOf(@[
childOfElementSelector(typeSelector(@"div")),
not(typeSelector(@"p"))
])
];

Here are more examples:

HTMLNode *firstDivElement = [document firstElementMatchingSelector:typeSelector(@"div")];
NSArray *secondChildOfDiv = [firstDivElement querySelectorAll:@":nth-child(2)"];
NSArray *secondOfType = [firstDivElement querySelectorAll:@":nth-of-type(2n)"];
secondChildOfDiv = [firstDivElement elementsMatchingSelector:nthChildSelector(CSSNthExpressionMake(0, 2))];
secondOfType = [firstDivElement elementsMatchingSelector:nthOfTypeSelector(CSSNthExpressionMake(2, 0))];
NSArray *notParagraphAndNotDiv = [firstDivElement querySelectorAll:@":not(p):not(div)"];
notParagraphAndNotDiv = [firstDivElement elementsMatchingSelector:allOf([
not(typeSelector(@"p")),
not(typeSelector(@"div"))
])
];

One 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:

CSSSelector *myAwesomeSelector = namedBlockSelector(@"myAwesomeSelector", ^BOOL (HTMLElement *element) {
return ![element.tagName isEqualToString:@"p"] && ![element.tagName isEqualToString:@"div"];
});
notParagraphAndNotDiv = [firstDivElement elementsMatchingSelector:myAwesomeSelector];

Change Log

See the CHANGELOG.md for more info.

License

HTMLKit is available under the MIT license. See the LICENSE file for more info.

About

An Objective-C framework for your everyday HTML needs.

Resources

Stars

0 stars

Watchers

3 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Repository files navigation

HTMLKit

HTMLKit Logo

An Objective-C framework for your everyday HTML needs.

HTMLKit CIcodecovCarthage CompatibleCocoaPods CompatiblePlatformLicense MIT

Quick Overview

HTMLKit is a WHATWG specification-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.

HTMLKit provides a rich DOM implementation for manipulating and navigating the document tree. It also understands CSS3 selectors making node-selection and querying the DOM a piece of cake.

DOM Validation

DOM mutations are validated as described in the WHATWG DOM Standard. 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.

Tests

HTMLKit passes all of the HTML5Lib 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.

The CSS3 Selector implementation is tested with an adapted version of the CSS3 Selectors Test Suite, ignoring the tests that require user interaction, session history, and scripting.

Does it Swift?

Check out the playground!

Installation

Carthage

Carthage is a decentralized dependency manager that builds your dependencies and provides you with binary frameworks.

If you don't have Carthage yet, you can install it with Homebrew using the following command:

$ brew update
$ brew install carthage

To add HTMLKit as a dependency into your project using Carthage just add the following line in your Cartfile:

github "iabudiab/HTMLKit"

Then run the following command to build the framework and drag the built HTMLKit.framework into your Xcode project.

$ carthage update

CocoaPods

CocoaPods is a dependency manager for Cocoa projects.

If you don't have CocoaPods yet, you can install it with the following command:

$ gem install cocoapods

To add HTMLKit as a dependency into your project using CocoaPods just add the following in your Podfile:

target'MyTarget'dopod'HTMLKit','~> 4.2'end

Then, run the following command:

$ pod install

Swift Package Manager

Swift Package Manager is the package manager for the Swift programming language.

Add HTMLKit to your Package.swift dependecies:

.package(url:"https://github.com/iabudiab/HTMLKit",.upToNextMajor(from:"4.0.0")),

Then run:

$ swift build

Manually

1- Add HTMLKit as git submodule

$ git submodule add https://github.com/iabudiab/HTMLKit.git

2- Open the HTMLKit folder and drag'n'drop the HTMLKit.xcodeproj into the Project Navigator in Xcode to add it as a sub-project.

3- In the General panel of your target add HTMLKit.framework under the Embedded Binaries

Parsing

Parsing Documents

Given some HTML content, you can parse it either via the HTMLParser or instatiate a HTMLDocument directly:

NSString *htmlString = @"<div><h1>HTMLKit</h1><p>Hello there!</p></div>";
// Via parser
HTMLParser *parser = [[HTMLParser alloc] initWithString:htmlString];
HTMLDocument *document = [parser parseDocument];
// Via static initializer
HTMLDocument *document = [HTMLDocument documentWithString:htmlString];

Parsing Fragments

You can also prase HTML content as a document fragment with a specified context element:

NSString *htmlString = @"<div><h1>HTMLKit</h1><p>Hello there!</p></div>";
HTMLParser *parser = [[HTMLParser alloc] initWithString: htmlString];
HTMLElement *tableContext = [[HTMLElement alloc] initWithTagName:@"table"];
NSArray *nodes = [parser parseFragmentWithContextElement:tableContext];
for (HTMLNode *node in nodes) {
NSLog(@"%@", node.outerHTML);
}
// The same parser instance can be reusued:
HTMLElement *bodyContext = [[HTMLElement alloc] initWithTagName:@"body"];
nodes = [parser parseFragmentWithContextElement:bodyContext];

The DOM

The DOM tree can be manipulated in several ways, here are just a few:

  • Create new elements and assign attributes
HTMLElement *description = [[HTMLElement alloc] initWithTagName:@"meta"attributes: @{@"name": @"description"}];
description[@"content"] = @"HTMLKit for iOS & OSX";
  • Append nodes to the document
HTMLElement *head = document.head;
[head appendNode:description];
HTMLElement *body = document.body;
NSArray *nodes = @[
[[HTMLElement alloc] initWithTagName:@"div"attributes: @{@"class": @"red"}],
[[HTMLElement alloc] initWithTagName:@"div"attributes: @{@"class": @"green"}],
[[HTMLElement alloc] initWithTagName:@"div"attributes: @{@"class": @"blue"}]
];
[body appendNodes:nodes];
  • Enumerate child elements and perform DOM editing
[body enumerateChildElementsUsingBlock:^(HTMLElement *element, NSUInteger idx, BOOL *stop) {
if ([element.tagName isEqualToString:@"div"]) {
HTMLElement *lorem = [[HTMLElement alloc] initWithTagName:@"p"];
lorem.textContent = [NSStringstringWithFormat:@"Lorem ipsum: %lu", (unsignedlong)idx];
[element appendNode:lorem];
}
}];
  • Remove nodes from the document
[body removeChildNodeAtIndex:1];
[head removeAllChildNodes];
[body.lastChild removeFromParentNode];
  • Manipulate the HTML directly
greenDiv.innerHTML = @"<ul><li>item 1<li>item 2";
  • Navigate to child and sibling nodes
HTMLNode *firstChild = body.firstChild;
HTMLNode *greenDiv = firstChild.nextSibling;
  • Iterate the DOM tree with custom filters
HTMLNodeFilterBlock *filter =[HTMLNodeFilterBlock filterWithBlock:^ HTMLNodeFilterValue (HTMLNode *node) {
if (node.childNodesCount != 1) {
return HTMLNodeFilterReject;
}
return HTMLNodeFilterAccept;
}];
for (HTMLElement *element in [body nodeIteratorWithShowOptions:HTMLNodeFilterShowElement filter:filter]) {
NSLog(@"%@", element.outerHTML);
}
  • Create and manipulate DOM Ranges
HTMLDocument *document = [HTMLDocument documentWithString:@"<div><h1>HTMLKit</h1><p id='foo'>Hello there!</p></div>"];
HTMLRange *range = [[HTMLRange alloc] initWithDocument:document];
HTMLNode *paragraph = [document querySelector:@"#foo"];
[range selectNode:paragraph];
[range extractContents];

CSS3 Selectors

All CSS3 Selectors are supported except for the pseudo-elements (::first-line, ::first-letter, ...etc.). You can use them the way you always have:

// Given the document:NSString *htmlString = @"<div><h1>HTMLKit</h1><p class='greeting'>Hello there!</p><p class='description'>This is a demo of HTMLKit</p></div>";
HTMLDocument *document = [HTMLDocument documentWithString: htmlString];
// Here are some of the supported selectorsNSArray *paragraphs = [document querySelectorAll:@"p"];
NSArray *paragraphsOrHeaders = [document querySelectorAll:@"p, h1"];
NSArray *hasClassAttribute = [document querySelectorAll:@"[class]"];
NSArray *greetings = [document querySelectorAll:@".greeting"];
NSArray *classNameStartsWith_de = [document querySelectorAll:@"[class^='de']"];
NSArray *hasAdjacentHeader = [document querySelectorAll:@"h1 + *"];
NSArray *hasSiblingHeader = [document querySelectorAll:@"h1 ~ *"];
NSArray *hasSiblingParagraph = [document querySelectorAll:@"p ~ *"];
NSArray *nonParagraphChildOfDiv = [document querySelectorAll:@"div :not(p)"];

HTMLKit 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:

NSArray *paragraphs = [document elementsMatchingSelector:typeSelector(@"p")];
NSArray *paragraphsOrHeaders = [document elementsMatchingSelector:anyOf(@[
typeSelector(@"p"), typeSelector(@"h1")
])
];
NSArray *hasClassAttribute = [document elementsMatchingSelector:hasAttributeSelector(@"class")];
NSArray *greetings = [document elementsMatchingSelector:classSelector(@"greeting")];
NSArray *classNameStartsWith_de = [document elementsMatchingSelector:attributeSelector(CSSAttributeSelectorBegins, @"class", @"de")];
NSArray *hasAdjacentHeader = [document elementsMatchingSelector:adjacentSiblingSelector(typeSelector(@"h1"))];
NSArray *hasSiblingHeader = [document elementsMatchingSelector:generalSiblingSelector(typeSelector(@"h1"))];
NSArray *hasSiblingParagraph = [document elementsMatchingSelector:generalSiblingSelector(typeSelector(@"p"))];
NSArray *nonParagraphChildOfDiv = [document elementsMatchingSelector:allOf(@[
childOfElementSelector(typeSelector(@"div")),
not(typeSelector(@"p"))
])
];

Here are more examples:

HTMLNode *firstDivElement = [document firstElementMatchingSelector:typeSelector(@"div")];
NSArray *secondChildOfDiv = [firstDivElement querySelectorAll:@":nth-child(2)"];
NSArray *secondOfType = [firstDivElement querySelectorAll:@":nth-of-type(2n)"];
secondChildOfDiv = [firstDivElement elementsMatchingSelector:nthChildSelector(CSSNthExpressionMake(0, 2))];
secondOfType = [firstDivElement elementsMatchingSelector:nthOfTypeSelector(CSSNthExpressionMake(2, 0))];
NSArray *notParagraphAndNotDiv = [firstDivElement querySelectorAll:@":not(p):not(div)"];
notParagraphAndNotDiv = [firstDivElement elementsMatchingSelector:allOf([
not(typeSelector(@"p")),
not(typeSelector(@"div"))
])
];

One 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:

CSSSelector *myAwesomeSelector = namedBlockSelector(@"myAwesomeSelector", ^BOOL (HTMLElement *element) {
return ![element.tagName isEqualToString:@"p"] && ![element.tagName isEqualToString:@"div"];
});
notParagraphAndNotDiv = [firstDivElement elementsMatchingSelector:myAwesomeSelector];

Change Log

See the CHANGELOG.md for more info.

License

HTMLKit is available under the MIT license. See the LICENSE file for more info.

About

An Objective-C framework for your everyday HTML needs.

Resources

Stars

0 stars

Watchers

3 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

HTMLKit

HTMLKit Logo

An Objective-C framework for your everyday HTML needs.

HTMLKit CIcodecovCarthage CompatibleCocoaPods CompatiblePlatformLicense MIT

Quick Overview

HTMLKit is a WHATWG specification-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.

HTMLKit provides a rich DOM implementation for manipulating and navigating the document tree. It also understands CSS3 selectors making node-selection and querying the DOM a piece of cake.

DOM Validation

DOM mutations are validated as described in the WHATWG DOM Standard. 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.

Tests

HTMLKit passes all of the HTML5Lib 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.

The CSS3 Selector implementation is tested with an adapted version of the CSS3 Selectors Test Suite, ignoring the tests that require user interaction, session history, and scripting.

Does it Swift?

Check out the playground!

Installation

Carthage

Carthage is a decentralized dependency manager that builds your dependencies and provides you with binary frameworks.

If you don't have Carthage yet, you can install it with Homebrew using the following command:

$ brew update
$ brew install carthage

To add HTMLKit as a dependency into your project using Carthage just add the following line in your Cartfile:

github "iabudiab/HTMLKit"

Then run the following command to build the framework and drag the built HTMLKit.framework into your Xcode project.

$ carthage update

CocoaPods

CocoaPods is a dependency manager for Cocoa projects.

If you don't have CocoaPods yet, you can install it with the following command:

$ gem install cocoapods

To add HTMLKit as a dependency into your project using CocoaPods just add the following in your Podfile:

target'MyTarget'dopod'HTMLKit','~> 4.2'end

Then, run the following command:

$ pod install

Swift Package Manager

Swift Package Manager is the package manager for the Swift programming language.

Add HTMLKit to your Package.swift dependecies:

.package(url:"https://github.com/iabudiab/HTMLKit",.upToNextMajor(from:"4.0.0")),

Then run:

$ swift build

Manually

1- Add HTMLKit as git submodule

$ git submodule add https://github.com/iabudiab/HTMLKit.git

2- Open the HTMLKit folder and drag'n'drop the HTMLKit.xcodeproj into the Project Navigator in Xcode to add it as a sub-project.

3- In the General panel of your target add HTMLKit.framework under the Embedded Binaries

Parsing

Parsing Documents

Given some HTML content, you can parse it either via the HTMLParser or instatiate a HTMLDocument directly:

NSString *htmlString = @"<div><h1>HTMLKit</h1><p>Hello there!</p></div>";
// Via parser
HTMLParser *parser = [[HTMLParser alloc] initWithString:htmlString];
HTMLDocument *document = [parser parseDocument];
// Via static initializer
HTMLDocument *document = [HTMLDocument documentWithString:htmlString];

Parsing Fragments

You can also prase HTML content as a document fragment with a specified context element:

NSString *htmlString = @"<div><h1>HTMLKit</h1><p>Hello there!</p></div>";
HTMLParser *parser = [[HTMLParser alloc] initWithString: htmlString];
HTMLElement *tableContext = [[HTMLElement alloc] initWithTagName:@"table"];
NSArray *nodes = [parser parseFragmentWithContextElement:tableContext];
for (HTMLNode *node in nodes) {
NSLog(@"%@", node.outerHTML);
}
// The same parser instance can be reusued:
HTMLElement *bodyContext = [[HTMLElement alloc] initWithTagName:@"body"];
nodes = [parser parseFragmentWithContextElement:bodyContext];

The DOM

The DOM tree can be manipulated in several ways, here are just a few:

  • Create new elements and assign attributes
HTMLElement *description = [[HTMLElement alloc] initWithTagName:@"meta"attributes: @{@"name": @"description"}];
description[@"content"] = @"HTMLKit for iOS & OSX";
  • Append nodes to the document
HTMLElement *head = document.head;
[head appendNode:description];
HTMLElement *body = document.body;
NSArray *nodes = @[
[[HTMLElement alloc] initWithTagName:@"div"attributes: @{@"class": @"red"}],
[[HTMLElement alloc] initWithTagName:@"div"attributes: @{@"class": @"green"}],
[[HTMLElement alloc] initWithTagName:@"div"attributes: @{@"class": @"blue"}]
];
[body appendNodes:nodes];
  • Enumerate child elements and perform DOM editing
[body enumerateChildElementsUsingBlock:^(HTMLElement *element, NSUInteger idx, BOOL *stop) {
if ([element.tagName isEqualToString:@"div"]) {
HTMLElement *lorem = [[HTMLElement alloc] initWithTagName:@"p"];
lorem.textContent = [NSStringstringWithFormat:@"Lorem ipsum: %lu", (unsignedlong)idx];
[element appendNode:lorem];
}
}];
  • Remove nodes from the document
[body removeChildNodeAtIndex:1];
[head removeAllChildNodes];
[body.lastChild removeFromParentNode];
  • Manipulate the HTML directly
greenDiv.innerHTML = @"<ul><li>item 1<li>item 2";
  • Navigate to child and sibling nodes
HTMLNode *firstChild = body.firstChild;
HTMLNode *greenDiv = firstChild.nextSibling;
  • Iterate the DOM tree with custom filters
HTMLNodeFilterBlock *filter =[HTMLNodeFilterBlock filterWithBlock:^ HTMLNodeFilterValue (HTMLNode *node) {
if (node.childNodesCount != 1) {
return HTMLNodeFilterReject;
}
return HTMLNodeFilterAccept;
}];
for (HTMLElement *element in [body nodeIteratorWithShowOptions:HTMLNodeFilterShowElement filter:filter]) {
NSLog(@"%@", element.outerHTML);
}
  • Create and manipulate DOM Ranges
HTMLDocument *document = [HTMLDocument documentWithString:@"<div><h1>HTMLKit</h1><p id='foo'>Hello there!</p></div>"];
HTMLRange *range = [[HTMLRange alloc] initWithDocument:document];
HTMLNode *paragraph = [document querySelector:@"#foo"];
[range selectNode:paragraph];
[range extractContents];

CSS3 Selectors

All CSS3 Selectors are supported except for the pseudo-elements (::first-line, ::first-letter, ...etc.). You can use them the way you always have:

// Given the document:NSString *htmlString = @"<div><h1>HTMLKit</h1><p class='greeting'>Hello there!</p><p class='description'>This is a demo of HTMLKit</p></div>";
HTMLDocument *document = [HTMLDocument documentWithString: htmlString];
// Here are some of the supported selectorsNSArray *paragraphs = [document querySelectorAll:@"p"];
NSArray *paragraphsOrHeaders = [document querySelectorAll:@"p, h1"];
NSArray *hasClassAttribute = [document querySelectorAll:@"[class]"];
NSArray *greetings = [document querySelectorAll:@".greeting"];
NSArray *classNameStartsWith_de = [document querySelectorAll:@"[class^='de']"];
NSArray *hasAdjacentHeader = [document querySelectorAll:@"h1 + *"];
NSArray *hasSiblingHeader = [document querySelectorAll:@"h1 ~ *"];
NSArray *hasSiblingParagraph = [document querySelectorAll:@"p ~ *"];
NSArray *nonParagraphChildOfDiv = [document querySelectorAll:@"div :not(p)"];

HTMLKit 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:

NSArray *paragraphs = [document elementsMatchingSelector:typeSelector(@"p")];
NSArray *paragraphsOrHeaders = [document elementsMatchingSelector:anyOf(@[
typeSelector(@"p"), typeSelector(@"h1")
])
];
NSArray *hasClassAttribute = [document elementsMatchingSelector:hasAttributeSelector(@"class")];
NSArray *greetings = [document elementsMatchingSelector:classSelector(@"greeting")];
NSArray *classNameStartsWith_de = [document elementsMatchingSelector:attributeSelector(CSSAttributeSelectorBegins, @"class", @"de")];
NSArray *hasAdjacentHeader = [document elementsMatchingSelector:adjacentSiblingSelector(typeSelector(@"h1"))];
NSArray *hasSiblingHeader = [document elementsMatchingSelector:generalSiblingSelector(typeSelector(@"h1"))];
NSArray *hasSiblingParagraph = [document elementsMatchingSelector:generalSiblingSelector(typeSelector(@"p"))];
NSArray *nonParagraphChildOfDiv = [document elementsMatchingSelector:allOf(@[
childOfElementSelector(typeSelector(@"div")),
not(typeSelector(@"p"))
])
];

Here are more examples:

HTMLNode *firstDivElement = [document firstElementMatchingSelector:typeSelector(@"div")];
NSArray *secondChildOfDiv = [firstDivElement querySelectorAll:@":nth-child(2)"];
NSArray *secondOfType = [firstDivElement querySelectorAll:@":nth-of-type(2n)"];
secondChildOfDiv = [firstDivElement elementsMatchingSelector:nthChildSelector(CSSNthExpressionMake(0, 2))];
secondOfType = [firstDivElement elementsMatchingSelector:nthOfTypeSelector(CSSNthExpressionMake(2, 0))];
NSArray *notParagraphAndNotDiv = [firstDivElement querySelectorAll:@":not(p):not(div)"];
notParagraphAndNotDiv = [firstDivElement elementsMatchingSelector:allOf([
not(typeSelector(@"p")),
not(typeSelector(@"div"))
])
];

One 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:

CSSSelector *myAwesomeSelector = namedBlockSelector(@"myAwesomeSelector", ^BOOL (HTMLElement *element) {
return ![element.tagName isEqualToString:@"p"] && ![element.tagName isEqualToString:@"div"];
});
notParagraphAndNotDiv = [firstDivElement elementsMatchingSelector:myAwesomeSelector];

Change Log

See the CHANGELOG.md for more info.

License

HTMLKit is available under the MIT license. See the LICENSE file for more info.

About

An Objective-C framework for your everyday HTML needs.

Resources

Stars

0 stars

Watchers

3 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

HTMLKit

HTMLKit Logo

An Objective-C framework for your everyday HTML needs.

HTMLKit CIcodecovCarthage CompatibleCocoaPods CompatiblePlatformLicense MIT

Quick Overview

HTMLKit is a WHATWG specification-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.

HTMLKit provides a rich DOM implementation for manipulating and navigating the document tree. It also understands CSS3 selectors making node-selection and querying the DOM a piece of cake.

DOM Validation

DOM mutations are validated as described in the WHATWG DOM Standard. 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.

Tests

HTMLKit passes all of the HTML5Lib 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.

The CSS3 Selector implementation is tested with an adapted version of the CSS3 Selectors Test Suite, ignoring the tests that require user interaction, session history, and scripting.

Does it Swift?

Check out the playground!

Installation

Carthage

Carthage is a decentralized dependency manager that builds your dependencies and provides you with binary frameworks.

If you don't have Carthage yet, you can install it with Homebrew using the following command:

$ brew update
$ brew install carthage

To add HTMLKit as a dependency into your project using Carthage just add the following line in your Cartfile:

github "iabudiab/HTMLKit"

Then run the following command to build the framework and drag the built HTMLKit.framework into your Xcode project.

$ carthage update

CocoaPods

CocoaPods is a dependency manager for Cocoa projects.

If you don't have CocoaPods yet, you can install it with the following command:

$ gem install cocoapods

To add HTMLKit as a dependency into your project using CocoaPods just add the following in your Podfile:

target'MyTarget'dopod'HTMLKit','~> 4.2'end

Then, run the following command:

$ pod install

Swift Package Manager

Swift Package Manager is the package manager for the Swift programming language.

Add HTMLKit to your Package.swift dependecies:

.package(url:"https://github.com/iabudiab/HTMLKit",.upToNextMajor(from:"4.0.0")),

Then run:

$ swift build

Manually

1- Add HTMLKit as git submodule

$ git submodule add https://github.com/iabudiab/HTMLKit.git

2- Open the HTMLKit folder and drag'n'drop the HTMLKit.xcodeproj into the Project Navigator in Xcode to add it as a sub-project.

3- In the General panel of your target add HTMLKit.framework under the Embedded Binaries

Parsing

Parsing Documents

Given some HTML content, you can parse it either via the HTMLParser or instatiate a HTMLDocument directly:

NSString *htmlString = @"<div><h1>HTMLKit</h1><p>Hello there!</p></div>";
// Via parser
HTMLParser *parser = [[HTMLParser alloc] initWithString:htmlString];
HTMLDocument *document = [parser parseDocument];
// Via static initializer
HTMLDocument *document = [HTMLDocument documentWithString:htmlString];

Parsing Fragments

You can also prase HTML content as a document fragment with a specified context element:

NSString *htmlString = @"<div><h1>HTMLKit</h1><p>Hello there!</p></div>";
HTMLParser *parser = [[HTMLParser alloc] initWithString: htmlString];
HTMLElement *tableContext = [[HTMLElement alloc] initWithTagName:@"table"];
NSArray *nodes = [parser parseFragmentWithContextElement:tableContext];
for (HTMLNode *node in nodes) {
NSLog(@"%@", node.outerHTML);
}
// The same parser instance can be reusued:
HTMLElement *bodyContext = [[HTMLElement alloc] initWithTagName:@"body"];
nodes = [parser parseFragmentWithContextElement:bodyContext];

The DOM

The DOM tree can be manipulated in several ways, here are just a few:

  • Create new elements and assign attributes
HTMLElement *description = [[HTMLElement alloc] initWithTagName:@"meta"attributes: @{@"name": @"description"}];
description[@"content"] = @"HTMLKit for iOS & OSX";
  • Append nodes to the document
HTMLElement *head = document.head;
[head appendNode:description];
HTMLElement *body = document.body;
NSArray *nodes = @[
[[HTMLElement alloc] initWithTagName:@"div"attributes: @{@"class": @"red"}],
[[HTMLElement alloc] initWithTagName:@"div"attributes: @{@"class": @"green"}],
[[HTMLElement alloc] initWithTagName:@"div"attributes: @{@"class": @"blue"}]
];
[body appendNodes:nodes];
  • Enumerate child elements and perform DOM editing
[body enumerateChildElementsUsingBlock:^(HTMLElement *element, NSUInteger idx, BOOL *stop) {
if ([element.tagName isEqualToString:@"div"]) {
HTMLElement *lorem = [[HTMLElement alloc] initWithTagName:@"p"];
lorem.textContent = [NSStringstringWithFormat:@"Lorem ipsum: %lu", (unsignedlong)idx];
[element appendNode:lorem];
}
}];
  • Remove nodes from the document
[body removeChildNodeAtIndex:1];
[head removeAllChildNodes];
[body.lastChild removeFromParentNode];
  • Manipulate the HTML directly
greenDiv.innerHTML = @"<ul><li>item 1<li>item 2";
  • Navigate to child and sibling nodes
HTMLNode *firstChild = body.firstChild;
HTMLNode *greenDiv = firstChild.nextSibling;
  • Iterate the DOM tree with custom filters
HTMLNodeFilterBlock *filter =[HTMLNodeFilterBlock filterWithBlock:^ HTMLNodeFilterValue (HTMLNode *node) {
if (node.childNodesCount != 1) {
return HTMLNodeFilterReject;
}
return HTMLNodeFilterAccept;
}];
for (HTMLElement *element in [body nodeIteratorWithShowOptions:HTMLNodeFilterShowElement filter:filter]) {
NSLog(@"%@", element.outerHTML);
}
  • Create and manipulate DOM Ranges
HTMLDocument *document = [HTMLDocument documentWithString:@"<div><h1>HTMLKit</h1><p id='foo'>Hello there!</p></div>"];
HTMLRange *range = [[HTMLRange alloc] initWithDocument:document];
HTMLNode *paragraph = [document querySelector:@"#foo"];
[range selectNode:paragraph];
[range extractContents];

CSS3 Selectors

All CSS3 Selectors are supported except for the pseudo-elements (::first-line, ::first-letter, ...etc.). You can use them the way you always have:

// Given the document:NSString *htmlString = @"<div><h1>HTMLKit</h1><p class='greeting'>Hello there!</p><p class='description'>This is a demo of HTMLKit</p></div>";
HTMLDocument *document = [HTMLDocument documentWithString: htmlString];
// Here are some of the supported selectorsNSArray *paragraphs = [document querySelectorAll:@"p"];
NSArray *paragraphsOrHeaders = [document querySelectorAll:@"p, h1"];
NSArray *hasClassAttribute = [document querySelectorAll:@"[class]"];
NSArray *greetings = [document querySelectorAll:@".greeting"];
NSArray *classNameStartsWith_de = [document querySelectorAll:@"[class^='de']"];
NSArray *hasAdjacentHeader = [document querySelectorAll:@"h1 + *"];
NSArray *hasSiblingHeader = [document querySelectorAll:@"h1 ~ *"];
NSArray *hasSiblingParagraph = [document querySelectorAll:@"p ~ *"];
NSArray *nonParagraphChildOfDiv = [document querySelectorAll:@"div :not(p)"];

HTMLKit 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:

NSArray *paragraphs = [document elementsMatchingSelector:typeSelector(@"p")];
NSArray *paragraphsOrHeaders = [document elementsMatchingSelector:anyOf(@[
typeSelector(@"p"), typeSelector(@"h1")
])
];
NSArray *hasClassAttribute = [document elementsMatchingSelector:hasAttributeSelector(@"class")];
NSArray *greetings = [document elementsMatchingSelector:classSelector(@"greeting")];
NSArray *classNameStartsWith_de = [document elementsMatchingSelector:attributeSelector(CSSAttributeSelectorBegins, @"class", @"de")];
NSArray *hasAdjacentHeader = [document elementsMatchingSelector:adjacentSiblingSelector(typeSelector(@"h1"))];
NSArray *hasSiblingHeader = [document elementsMatchingSelector:generalSiblingSelector(typeSelector(@"h1"))];
NSArray *hasSiblingParagraph = [document elementsMatchingSelector:generalSiblingSelector(typeSelector(@"p"))];
NSArray *nonParagraphChildOfDiv = [document elementsMatchingSelector:allOf(@[
childOfElementSelector(typeSelector(@"div")),
not(typeSelector(@"p"))
])
];

Here are more examples:

HTMLNode *firstDivElement = [document firstElementMatchingSelector:typeSelector(@"div")];
NSArray *secondChildOfDiv = [firstDivElement querySelectorAll:@":nth-child(2)"];
NSArray *secondOfType = [firstDivElement querySelectorAll:@":nth-of-type(2n)"];
secondChildOfDiv = [firstDivElement elementsMatchingSelector:nthChildSelector(CSSNthExpressionMake(0, 2))];
secondOfType = [firstDivElement elementsMatchingSelector:nthOfTypeSelector(CSSNthExpressionMake(2, 0))];
NSArray *notParagraphAndNotDiv = [firstDivElement querySelectorAll:@":not(p):not(div)"];
notParagraphAndNotDiv = [firstDivElement elementsMatchingSelector:allOf([
not(typeSelector(@"p")),
not(typeSelector(@"div"))
])
];

One 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:

CSSSelector *myAwesomeSelector = namedBlockSelector(@"myAwesomeSelector", ^BOOL (HTMLElement *element) {
return ![element.tagName isEqualToString:@"p"] && ![element.tagName isEqualToString:@"div"];
});
notParagraphAndNotDiv = [firstDivElement elementsMatchingSelector:myAwesomeSelector];

Change Log

See the CHANGELOG.md for more info.

License

HTMLKit is available under the MIT license. See the LICENSE file for more info.

About

An Objective-C framework for your everyday HTML needs.

Resources

Stars

0 stars

Watchers

3 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Repository files navigation

HTMLKit

HTMLKit Logo

An Objective-C framework for your everyday HTML needs.

HTMLKit CIcodecovCarthage CompatibleCocoaPods CompatiblePlatformLicense MIT

Quick Overview

HTMLKit is a WHATWG specification-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.

HTMLKit provides a rich DOM implementation for manipulating and navigating the document tree. It also understands CSS3 selectors making node-selection and querying the DOM a piece of cake.

DOM Validation

DOM mutations are validated as described in the WHATWG DOM Standard. 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.

Tests

HTMLKit passes all of the HTML5Lib 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.

The CSS3 Selector implementation is tested with an adapted version of the CSS3 Selectors Test Suite, ignoring the tests that require user interaction, session history, and scripting.

Does it Swift?

Check out the playground!

Installation

Carthage

Carthage is a decentralized dependency manager that builds your dependencies and provides you with binary frameworks.

If you don't have Carthage yet, you can install it with Homebrew using the following command:

$ brew update
$ brew install carthage

To add HTMLKit as a dependency into your project using Carthage just add the following line in your Cartfile:

github "iabudiab/HTMLKit"

Then run the following command to build the framework and drag the built HTMLKit.framework into your Xcode project.

$ carthage update

CocoaPods

CocoaPods is a dependency manager for Cocoa projects.

If you don't have CocoaPods yet, you can install it with the following command:

$ gem install cocoapods

To add HTMLKit as a dependency into your project using CocoaPods just add the following in your Podfile:

target'MyTarget'dopod'HTMLKit','~> 4.2'end

Then, run the following command:

$ pod install

Swift Package Manager

Swift Package Manager is the package manager for the Swift programming language.

Add HTMLKit to your Package.swift dependecies:

.package(url:"https://github.com/iabudiab/HTMLKit",.upToNextMajor(from:"4.0.0")),

Then run:

$ swift build

Manually

1- Add HTMLKit as git submodule

$ git submodule add https://github.com/iabudiab/HTMLKit.git

2- Open the HTMLKit folder and drag'n'drop the HTMLKit.xcodeproj into the Project Navigator in Xcode to add it as a sub-project.

3- In the General panel of your target add HTMLKit.framework under the Embedded Binaries

Parsing

Parsing Documents

Given some HTML content, you can parse it either via the HTMLParser or instatiate a HTMLDocument directly:

NSString *htmlString = @"<div><h1>HTMLKit</h1><p>Hello there!</p></div>";
// Via parser
HTMLParser *parser = [[HTMLParser alloc] initWithString:htmlString];
HTMLDocument *document = [parser parseDocument];
// Via static initializer
HTMLDocument *document = [HTMLDocument documentWithString:htmlString];

Parsing Fragments

You can also prase HTML content as a document fragment with a specified context element:

NSString *htmlString = @"<div><h1>HTMLKit</h1><p>Hello there!</p></div>";
HTMLParser *parser = [[HTMLParser alloc] initWithString: htmlString];
HTMLElement *tableContext = [[HTMLElement alloc] initWithTagName:@"table"];
NSArray *nodes = [parser parseFragmentWithContextElement:tableContext];
for (HTMLNode *node in nodes) {
NSLog(@"%@", node.outerHTML);
}
// The same parser instance can be reusued:
HTMLElement *bodyContext = [[HTMLElement alloc] initWithTagName:@"body"];
nodes = [parser parseFragmentWithContextElement:bodyContext];

The DOM

The DOM tree can be manipulated in several ways, here are just a few:

  • Create new elements and assign attributes
HTMLElement *description = [[HTMLElement alloc] initWithTagName:@"meta"attributes: @{@"name": @"description"}];
description[@"content"] = @"HTMLKit for iOS & OSX";
  • Append nodes to the document
HTMLElement *head = document.head;
[head appendNode:description];
HTMLElement *body = document.body;
NSArray *nodes = @[
[[HTMLElement alloc] initWithTagName:@"div"attributes: @{@"class": @"red"}],
[[HTMLElement alloc] initWithTagName:@"div"attributes: @{@"class": @"green"}],
[[HTMLElement alloc] initWithTagName:@"div"attributes: @{@"class": @"blue"}]
];
[body appendNodes:nodes];
  • Enumerate child elements and perform DOM editing
[body enumerateChildElementsUsingBlock:^(HTMLElement *element, NSUInteger idx, BOOL *stop) {
if ([element.tagName isEqualToString:@"div"]) {
HTMLElement *lorem = [[HTMLElement alloc] initWithTagName:@"p"];
lorem.textContent = [NSStringstringWithFormat:@"Lorem ipsum: %lu", (unsignedlong)idx];
[element appendNode:lorem];
}
}];
  • Remove nodes from the document
[body removeChildNodeAtIndex:1];
[head removeAllChildNodes];
[body.lastChild removeFromParentNode];
  • Manipulate the HTML directly
greenDiv.innerHTML = @"<ul><li>item 1<li>item 2";
  • Navigate to child and sibling nodes
HTMLNode *firstChild = body.firstChild;
HTMLNode *greenDiv = firstChild.nextSibling;
  • Iterate the DOM tree with custom filters
HTMLNodeFilterBlock *filter =[HTMLNodeFilterBlock filterWithBlock:^ HTMLNodeFilterValue (HTMLNode *node) {
if (node.childNodesCount != 1) {
return HTMLNodeFilterReject;
}
return HTMLNodeFilterAccept;
}];
for (HTMLElement *element in [body nodeIteratorWithShowOptions:HTMLNodeFilterShowElement filter:filter]) {
NSLog(@"%@", element.outerHTML);
}
  • Create and manipulate DOM Ranges
HTMLDocument *document = [HTMLDocument documentWithString:@"<div><h1>HTMLKit</h1><p id='foo'>Hello there!</p></div>"];
HTMLRange *range = [[HTMLRange alloc] initWithDocument:document];
HTMLNode *paragraph = [document querySelector:@"#foo"];
[range selectNode:paragraph];
[range extractContents];

CSS3 Selectors

All CSS3 Selectors are supported except for the pseudo-elements (::first-line, ::first-letter, ...etc.). You can use them the way you always have:

// Given the document:NSString *htmlString = @"<div><h1>HTMLKit</h1><p class='greeting'>Hello there!</p><p class='description'>This is a demo of HTMLKit</p></div>";
HTMLDocument *document = [HTMLDocument documentWithString: htmlString];
// Here are some of the supported selectorsNSArray *paragraphs = [document querySelectorAll:@"p"];
NSArray *paragraphsOrHeaders = [document querySelectorAll:@"p, h1"];
NSArray *hasClassAttribute = [document querySelectorAll:@"[class]"];
NSArray *greetings = [document querySelectorAll:@".greeting"];
NSArray *classNameStartsWith_de = [document querySelectorAll:@"[class^='de']"];
NSArray *hasAdjacentHeader = [document querySelectorAll:@"h1 + *"];
NSArray *hasSiblingHeader = [document querySelectorAll:@"h1 ~ *"];
NSArray *hasSiblingParagraph = [document querySelectorAll:@"p ~ *"];
NSArray *nonParagraphChildOfDiv = [document querySelectorAll:@"div :not(p)"];

HTMLKit 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:

NSArray *paragraphs = [document elementsMatchingSelector:typeSelector(@"p")];
NSArray *paragraphsOrHeaders = [document elementsMatchingSelector:anyOf(@[
typeSelector(@"p"), typeSelector(@"h1")
])
];
NSArray *hasClassAttribute = [document elementsMatchingSelector:hasAttributeSelector(@"class")];
NSArray *greetings = [document elementsMatchingSelector:classSelector(@"greeting")];
NSArray *classNameStartsWith_de = [document elementsMatchingSelector:attributeSelector(CSSAttributeSelectorBegins, @"class", @"de")];
NSArray *hasAdjacentHeader = [document elementsMatchingSelector:adjacentSiblingSelector(typeSelector(@"h1"))];
NSArray *hasSiblingHeader = [document elementsMatchingSelector:generalSiblingSelector(typeSelector(@"h1"))];
NSArray *hasSiblingParagraph = [document elementsMatchingSelector:generalSiblingSelector(typeSelector(@"p"))];
NSArray *nonParagraphChildOfDiv = [document elementsMatchingSelector:allOf(@[
childOfElementSelector(typeSelector(@"div")),
not(typeSelector(@"p"))
])
];

Here are more examples:

HTMLNode *firstDivElement = [document firstElementMatchingSelector:typeSelector(@"div")];
NSArray *secondChildOfDiv = [firstDivElement querySelectorAll:@":nth-child(2)"];
NSArray *secondOfType = [firstDivElement querySelectorAll:@":nth-of-type(2n)"];
secondChildOfDiv = [firstDivElement elementsMatchingSelector:nthChildSelector(CSSNthExpressionMake(0, 2))];
secondOfType = [firstDivElement elementsMatchingSelector:nthOfTypeSelector(CSSNthExpressionMake(2, 0))];
NSArray *notParagraphAndNotDiv = [firstDivElement querySelectorAll:@":not(p):not(div)"];
notParagraphAndNotDiv = [firstDivElement elementsMatchingSelector:allOf([
not(typeSelector(@"p")),
not(typeSelector(@"div"))
])
];

One 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:

CSSSelector *myAwesomeSelector = namedBlockSelector(@"myAwesomeSelector", ^BOOL (HTMLElement *element) {
return ![element.tagName isEqualToString:@"p"] && ![element.tagName isEqualToString:@"div"];
});
notParagraphAndNotDiv = [firstDivElement elementsMatchingSelector:myAwesomeSelector];

Change Log

See the CHANGELOG.md for more info.

License

HTMLKit is available under the MIT license. See the LICENSE file for more info.

About

An Objective-C framework for your everyday HTML needs.

Resources

Stars

0 stars

Watchers

3 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

HTMLKit

HTMLKit Logo

An Objective-C framework for your everyday HTML needs.

HTMLKit CIcodecovCarthage CompatibleCocoaPods CompatiblePlatformLicense MIT

Quick Overview

HTMLKit is a WHATWG specification-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.

HTMLKit provides a rich DOM implementation for manipulating and navigating the document tree. It also understands CSS3 selectors making node-selection and querying the DOM a piece of cake.

DOM Validation

DOM mutations are validated as described in the WHATWG DOM Standard. 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.

Tests

HTMLKit passes all of the HTML5Lib 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.

The CSS3 Selector implementation is tested with an adapted version of the CSS3 Selectors Test Suite, ignoring the tests that require user interaction, session history, and scripting.

Does it Swift?

Check out the playground!

Installation

Carthage

Carthage is a decentralized dependency manager that builds your dependencies and provides you with binary frameworks.

If you don't have Carthage yet, you can install it with Homebrew using the following command:

$ brew update
$ brew install carthage

To add HTMLKit as a dependency into your project using Carthage just add the following line in your Cartfile:

github "iabudiab/HTMLKit"

Then run the following command to build the framework and drag the built HTMLKit.framework into your Xcode project.

$ carthage update

CocoaPods

CocoaPods is a dependency manager for Cocoa projects.

If you don't have CocoaPods yet, you can install it with the following command:

$ gem install cocoapods

To add HTMLKit as a dependency into your project using CocoaPods just add the following in your Podfile:

target'MyTarget'dopod'HTMLKit','~> 4.2'end

Then, run the following command:

$ pod install

Swift Package Manager

Swift Package Manager is the package manager for the Swift programming language.

Add HTMLKit to your Package.swift dependecies:

.package(url:"https://github.com/iabudiab/HTMLKit",.upToNextMajor(from:"4.0.0")),

Then run:

$ swift build

Manually

1- Add HTMLKit as git submodule

$ git submodule add https://github.com/iabudiab/HTMLKit.git

2- Open the HTMLKit folder and drag'n'drop the HTMLKit.xcodeproj into the Project Navigator in Xcode to add it as a sub-project.

3- In the General panel of your target add HTMLKit.framework under the Embedded Binaries

Parsing

Parsing Documents

Given some HTML content, you can parse it either via the HTMLParser or instatiate a HTMLDocument directly:

NSString *htmlString = @"<div><h1>HTMLKit</h1><p>Hello there!</p></div>";
// Via parser
HTMLParser *parser = [[HTMLParser alloc] initWithString:htmlString];
HTMLDocument *document = [parser parseDocument];
// Via static initializer
HTMLDocument *document = [HTMLDocument documentWithString:htmlString];

Parsing Fragments

You can also prase HTML content as a document fragment with a specified context element:

NSString *htmlString = @"<div><h1>HTMLKit</h1><p>Hello there!</p></div>";
HTMLParser *parser = [[HTMLParser alloc] initWithString: htmlString];
HTMLElement *tableContext = [[HTMLElement alloc] initWithTagName:@"table"];
NSArray *nodes = [parser parseFragmentWithContextElement:tableContext];
for (HTMLNode *node in nodes) {
NSLog(@"%@", node.outerHTML);
}
// The same parser instance can be reusued:
HTMLElement *bodyContext = [[HTMLElement alloc] initWithTagName:@"body"];
nodes = [parser parseFragmentWithContextElement:bodyContext];

The DOM

The DOM tree can be manipulated in several ways, here are just a few:

  • Create new elements and assign attributes
HTMLElement *description = [[HTMLElement alloc] initWithTagName:@"meta"attributes: @{@"name": @"description"}];
description[@"content"] = @"HTMLKit for iOS & OSX";
  • Append nodes to the document
HTMLElement *head = document.head;
[head appendNode:description];
HTMLElement *body = document.body;
NSArray *nodes = @[
[[HTMLElement alloc] initWithTagName:@"div"attributes: @{@"class": @"red"}],
[[HTMLElement alloc] initWithTagName:@"div"attributes: @{@"class": @"green"}],
[[HTMLElement alloc] initWithTagName:@"div"attributes: @{@"class": @"blue"}]
];
[body appendNodes:nodes];
  • Enumerate child elements and perform DOM editing
[body enumerateChildElementsUsingBlock:^(HTMLElement *element, NSUInteger idx, BOOL *stop) {
if ([element.tagName isEqualToString:@"div"]) {
HTMLElement *lorem = [[HTMLElement alloc] initWithTagName:@"p"];
lorem.textContent = [NSStringstringWithFormat:@"Lorem ipsum: %lu", (unsignedlong)idx];
[element appendNode:lorem];
}
}];
  • Remove nodes from the document
[body removeChildNodeAtIndex:1];
[head removeAllChildNodes];
[body.lastChild removeFromParentNode];
  • Manipulate the HTML directly
greenDiv.innerHTML = @"<ul><li>item 1<li>item 2";
  • Navigate to child and sibling nodes
HTMLNode *firstChild = body.firstChild;
HTMLNode *greenDiv = firstChild.nextSibling;
  • Iterate the DOM tree with custom filters
HTMLNodeFilterBlock *filter =[HTMLNodeFilterBlock filterWithBlock:^ HTMLNodeFilterValue (HTMLNode *node) {
if (node.childNodesCount != 1) {
return HTMLNodeFilterReject;
}
return HTMLNodeFilterAccept;
}];
for (HTMLElement *element in [body nodeIteratorWithShowOptions:HTMLNodeFilterShowElement filter:filter]) {
NSLog(@"%@", element.outerHTML);
}
  • Create and manipulate DOM Ranges
HTMLDocument *document = [HTMLDocument documentWithString:@"<div><h1>HTMLKit</h1><p id='foo'>Hello there!</p></div>"];
HTMLRange *range = [[HTMLRange alloc] initWithDocument:document];
HTMLNode *paragraph = [document querySelector:@"#foo"];
[range selectNode:paragraph];
[range extractContents];

CSS3 Selectors

All CSS3 Selectors are supported except for the pseudo-elements (::first-line, ::first-letter, ...etc.). You can use them the way you always have:

// Given the document:NSString *htmlString = @"<div><h1>HTMLKit</h1><p class='greeting'>Hello there!</p><p class='description'>This is a demo of HTMLKit</p></div>";
HTMLDocument *document = [HTMLDocument documentWithString: htmlString];
// Here are some of the supported selectorsNSArray *paragraphs = [document querySelectorAll:@"p"];
NSArray *paragraphsOrHeaders = [document querySelectorAll:@"p, h1"];
NSArray *hasClassAttribute = [document querySelectorAll:@"[class]"];
NSArray *greetings = [document querySelectorAll:@".greeting"];
NSArray *classNameStartsWith_de = [document querySelectorAll:@"[class^='de']"];
NSArray *hasAdjacentHeader = [document querySelectorAll:@"h1 + *"];
NSArray *hasSiblingHeader = [document querySelectorAll:@"h1 ~ *"];
NSArray *hasSiblingParagraph = [document querySelectorAll:@"p ~ *"];
NSArray *nonParagraphChildOfDiv = [document querySelectorAll:@"div :not(p)"];

HTMLKit 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:

NSArray *paragraphs = [document elementsMatchingSelector:typeSelector(@"p")];
NSArray *paragraphsOrHeaders = [document elementsMatchingSelector:anyOf(@[
typeSelector(@"p"), typeSelector(@"h1")
])
];
NSArray *hasClassAttribute = [document elementsMatchingSelector:hasAttributeSelector(@"class")];
NSArray *greetings = [document elementsMatchingSelector:classSelector(@"greeting")];
NSArray *classNameStartsWith_de = [document elementsMatchingSelector:attributeSelector(CSSAttributeSelectorBegins, @"class", @"de")];
NSArray *hasAdjacentHeader = [document elementsMatchingSelector:adjacentSiblingSelector(typeSelector(@"h1"))];
NSArray *hasSiblingHeader = [document elementsMatchingSelector:generalSiblingSelector(typeSelector(@"h1"))];
NSArray *hasSiblingParagraph = [document elementsMatchingSelector:generalSiblingSelector(typeSelector(@"p"))];
NSArray *nonParagraphChildOfDiv = [document elementsMatchingSelector:allOf(@[
childOfElementSelector(typeSelector(@"div")),
not(typeSelector(@"p"))
])
];

Here are more examples:

HTMLNode *firstDivElement = [document firstElementMatchingSelector:typeSelector(@"div")];
NSArray *secondChildOfDiv = [firstDivElement querySelectorAll:@":nth-child(2)"];
NSArray *secondOfType = [firstDivElement querySelectorAll:@":nth-of-type(2n)"];
secondChildOfDiv = [firstDivElement elementsMatchingSelector:nthChildSelector(CSSNthExpressionMake(0, 2))];
secondOfType = [firstDivElement elementsMatchingSelector:nthOfTypeSelector(CSSNthExpressionMake(2, 0))];
NSArray *notParagraphAndNotDiv = [firstDivElement querySelectorAll:@":not(p):not(div)"];
notParagraphAndNotDiv = [firstDivElement elementsMatchingSelector:allOf([
not(typeSelector(@"p")),
not(typeSelector(@"div"))
])
];

One 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:

CSSSelector *myAwesomeSelector = namedBlockSelector(@"myAwesomeSelector", ^BOOL (HTMLElement *element) {
return ![element.tagName isEqualToString:@"p"] && ![element.tagName isEqualToString:@"div"];
});
notParagraphAndNotDiv = [firstDivElement elementsMatchingSelector:myAwesomeSelector];

Change Log

See the CHANGELOG.md for more info.

License

HTMLKit is available under the MIT license. See the LICENSE file for more info.

About

An Objective-C framework for your everyday HTML needs.

Resources

Stars

0 stars

Watchers

3 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

HTMLKit

HTMLKit Logo

An Objective-C framework for your everyday HTML needs.

HTMLKit CIcodecovCarthage CompatibleCocoaPods CompatiblePlatformLicense MIT

Quick Overview

HTMLKit is a WHATWG specification-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.

HTMLKit provides a rich DOM implementation for manipulating and navigating the document tree. It also understands CSS3 selectors making node-selection and querying the DOM a piece of cake.

DOM Validation

DOM mutations are validated as described in the WHATWG DOM Standard. 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.

Tests

HTMLKit passes all of the HTML5Lib 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.

The CSS3 Selector implementation is tested with an adapted version of the CSS3 Selectors Test Suite, ignoring the tests that require user interaction, session history, and scripting.

Does it Swift?

Check out the playground!

Installation

Carthage

Carthage is a decentralized dependency manager that builds your dependencies and provides you with binary frameworks.

If you don't have Carthage yet, you can install it with Homebrew using the following command:

$ brew update
$ brew install carthage

To add HTMLKit as a dependency into your project using Carthage just add the following line in your Cartfile:

github "iabudiab/HTMLKit"

Then run the following command to build the framework and drag the built HTMLKit.framework into your Xcode project.

$ carthage update

CocoaPods

CocoaPods is a dependency manager for Cocoa projects.

If you don't have CocoaPods yet, you can install it with the following command:

$ gem install cocoapods

To add HTMLKit as a dependency into your project using CocoaPods just add the following in your Podfile:

target'MyTarget'dopod'HTMLKit','~> 4.2'end

Then, run the following command:

$ pod install

Swift Package Manager

Swift Package Manager is the package manager for the Swift programming language.

Add HTMLKit to your Package.swift dependecies:

.package(url:"https://github.com/iabudiab/HTMLKit",.upToNextMajor(from:"4.0.0")),

Then run:

$ swift build

Manually

1- Add HTMLKit as git submodule

$ git submodule add https://github.com/iabudiab/HTMLKit.git

2- Open the HTMLKit folder and drag'n'drop the HTMLKit.xcodeproj into the Project Navigator in Xcode to add it as a sub-project.

3- In the General panel of your target add HTMLKit.framework under the Embedded Binaries

Parsing

Parsing Documents

Given some HTML content, you can parse it either via the HTMLParser or instatiate a HTMLDocument directly:

NSString *htmlString = @"<div><h1>HTMLKit</h1><p>Hello there!</p></div>";
// Via parser
HTMLParser *parser = [[HTMLParser alloc] initWithString:htmlString];
HTMLDocument *document = [parser parseDocument];
// Via static initializer
HTMLDocument *document = [HTMLDocument documentWithString:htmlString];

Parsing Fragments

You can also prase HTML content as a document fragment with a specified context element:

NSString *htmlString = @"<div><h1>HTMLKit</h1><p>Hello there!</p></div>";
HTMLParser *parser = [[HTMLParser alloc] initWithString: htmlString];
HTMLElement *tableContext = [[HTMLElement alloc] initWithTagName:@"table"];
NSArray *nodes = [parser parseFragmentWithContextElement:tableContext];
for (HTMLNode *node in nodes) {
NSLog(@"%@", node.outerHTML);
}
// The same parser instance can be reusued:
HTMLElement *bodyContext = [[HTMLElement alloc] initWithTagName:@"body"];
nodes = [parser parseFragmentWithContextElement:bodyContext];

The DOM

The DOM tree can be manipulated in several ways, here are just a few:

  • Create new elements and assign attributes
HTMLElement *description = [[HTMLElement alloc] initWithTagName:@"meta"attributes: @{@"name": @"description"}];
description[@"content"] = @"HTMLKit for iOS & OSX";
  • Append nodes to the document
HTMLElement *head = document.head;
[head appendNode:description];
HTMLElement *body = document.body;
NSArray *nodes = @[
[[HTMLElement alloc] initWithTagName:@"div"attributes: @{@"class": @"red"}],
[[HTMLElement alloc] initWithTagName:@"div"attributes: @{@"class": @"green"}],
[[HTMLElement alloc] initWithTagName:@"div"attributes: @{@"class": @"blue"}]
];
[body appendNodes:nodes];
  • Enumerate child elements and perform DOM editing
[body enumerateChildElementsUsingBlock:^(HTMLElement *element, NSUInteger idx, BOOL *stop) {
if ([element.tagName isEqualToString:@"div"]) {
HTMLElement *lorem = [[HTMLElement alloc] initWithTagName:@"p"];
lorem.textContent = [NSStringstringWithFormat:@"Lorem ipsum: %lu", (unsignedlong)idx];
[element appendNode:lorem];
}
}];
  • Remove nodes from the document
[body removeChildNodeAtIndex:1];
[head removeAllChildNodes];
[body.lastChild removeFromParentNode];
  • Manipulate the HTML directly
greenDiv.innerHTML = @"<ul><li>item 1<li>item 2";
  • Navigate to child and sibling nodes
HTMLNode *firstChild = body.firstChild;
HTMLNode *greenDiv = firstChild.nextSibling;
  • Iterate the DOM tree with custom filters
HTMLNodeFilterBlock *filter =[HTMLNodeFilterBlock filterWithBlock:^ HTMLNodeFilterValue (HTMLNode *node) {
if (node.childNodesCount != 1) {
return HTMLNodeFilterReject;
}
return HTMLNodeFilterAccept;
}];
for (HTMLElement *element in [body nodeIteratorWithShowOptions:HTMLNodeFilterShowElement filter:filter]) {
NSLog(@"%@", element.outerHTML);
}
  • Create and manipulate DOM Ranges
HTMLDocument *document = [HTMLDocument documentWithString:@"<div><h1>HTMLKit</h1><p id='foo'>Hello there!</p></div>"];
HTMLRange *range = [[HTMLRange alloc] initWithDocument:document];
HTMLNode *paragraph = [document querySelector:@"#foo"];
[range selectNode:paragraph];
[range extractContents];

CSS3 Selectors

All CSS3 Selectors are supported except for the pseudo-elements (::first-line, ::first-letter, ...etc.). You can use them the way you always have:

// Given the document:NSString *htmlString = @"<div><h1>HTMLKit</h1><p class='greeting'>Hello there!</p><p class='description'>This is a demo of HTMLKit</p></div>";
HTMLDocument *document = [HTMLDocument documentWithString: htmlString];
// Here are some of the supported selectorsNSArray *paragraphs = [document querySelectorAll:@"p"];
NSArray *paragraphsOrHeaders = [document querySelectorAll:@"p, h1"];
NSArray *hasClassAttribute = [document querySelectorAll:@"[class]"];
NSArray *greetings = [document querySelectorAll:@".greeting"];
NSArray *classNameStartsWith_de = [document querySelectorAll:@"[class^='de']"];
NSArray *hasAdjacentHeader = [document querySelectorAll:@"h1 + *"];
NSArray *hasSiblingHeader = [document querySelectorAll:@"h1 ~ *"];
NSArray *hasSiblingParagraph = [document querySelectorAll:@"p ~ *"];
NSArray *nonParagraphChildOfDiv = [document querySelectorAll:@"div :not(p)"];

HTMLKit 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:

NSArray *paragraphs = [document elementsMatchingSelector:typeSelector(@"p")];
NSArray *paragraphsOrHeaders = [document elementsMatchingSelector:anyOf(@[
typeSelector(@"p"), typeSelector(@"h1")
])
];
NSArray *hasClassAttribute = [document elementsMatchingSelector:hasAttributeSelector(@"class")];
NSArray *greetings = [document elementsMatchingSelector:classSelector(@"greeting")];
NSArray *classNameStartsWith_de = [document elementsMatchingSelector:attributeSelector(CSSAttributeSelectorBegins, @"class", @"de")];
NSArray *hasAdjacentHeader = [document elementsMatchingSelector:adjacentSiblingSelector(typeSelector(@"h1"))];
NSArray *hasSiblingHeader = [document elementsMatchingSelector:generalSiblingSelector(typeSelector(@"h1"))];
NSArray *hasSiblingParagraph = [document elementsMatchingSelector:generalSiblingSelector(typeSelector(@"p"))];
NSArray *nonParagraphChildOfDiv = [document elementsMatchingSelector:allOf(@[
childOfElementSelector(typeSelector(@"div")),
not(typeSelector(@"p"))
])
];

Here are more examples:

HTMLNode *firstDivElement = [document firstElementMatchingSelector:typeSelector(@"div")];
NSArray *secondChildOfDiv = [firstDivElement querySelectorAll:@":nth-child(2)"];
NSArray *secondOfType = [firstDivElement querySelectorAll:@":nth-of-type(2n)"];
secondChildOfDiv = [firstDivElement elementsMatchingSelector:nthChildSelector(CSSNthExpressionMake(0, 2))];
secondOfType = [firstDivElement elementsMatchingSelector:nthOfTypeSelector(CSSNthExpressionMake(2, 0))];
NSArray *notParagraphAndNotDiv = [firstDivElement querySelectorAll:@":not(p):not(div)"];
notParagraphAndNotDiv = [firstDivElement elementsMatchingSelector:allOf([
not(typeSelector(@"p")),
not(typeSelector(@"div"))
])
];

One 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:

CSSSelector *myAwesomeSelector = namedBlockSelector(@"myAwesomeSelector", ^BOOL (HTMLElement *element) {
return ![element.tagName isEqualToString:@"p"] && ![element.tagName isEqualToString:@"div"];
});
notParagraphAndNotDiv = [firstDivElement elementsMatchingSelector:myAwesomeSelector];

Change Log

See the CHANGELOG.md for more info.

License

HTMLKit is available under the MIT license. See the LICENSE file for more info.

About

An Objective-C framework for your everyday HTML needs.

Resources

Stars

0 stars

Watchers

3 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Repository files navigation

HTMLKit

HTMLKit Logo

An Objective-C framework for your everyday HTML needs.

HTMLKit CIcodecovCarthage CompatibleCocoaPods CompatiblePlatformLicense MIT

Quick Overview

HTMLKit is a WHATWG specification-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.

HTMLKit provides a rich DOM implementation for manipulating and navigating the document tree. It also understands CSS3 selectors making node-selection and querying the DOM a piece of cake.

DOM Validation

DOM mutations are validated as described in the WHATWG DOM Standard. 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.

Tests

HTMLKit passes all of the HTML5Lib 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.

The CSS3 Selector implementation is tested with an adapted version of the CSS3 Selectors Test Suite, ignoring the tests that require user interaction, session history, and scripting.

Does it Swift?

Check out the playground!

Installation

Carthage

Carthage is a decentralized dependency manager that builds your dependencies and provides you with binary frameworks.

If you don't have Carthage yet, you can install it with Homebrew using the following command:

$ brew update
$ brew install carthage

To add HTMLKit as a dependency into your project using Carthage just add the following line in your Cartfile:

github "iabudiab/HTMLKit"

Then run the following command to build the framework and drag the built HTMLKit.framework into your Xcode project.

$ carthage update

CocoaPods

CocoaPods is a dependency manager for Cocoa projects.

If you don't have CocoaPods yet, you can install it with the following command:

$ gem install cocoapods

To add HTMLKit as a dependency into your project using CocoaPods just add the following in your Podfile:

target'MyTarget'dopod'HTMLKit','~> 4.2'end

Then, run the following command:

$ pod install

Swift Package Manager

Swift Package Manager is the package manager for the Swift programming language.

Add HTMLKit to your Package.swift dependecies:

.package(url:"https://github.com/iabudiab/HTMLKit",.upToNextMajor(from:"4.0.0")),

Then run:

$ swift build

Manually

1- Add HTMLKit as git submodule

$ git submodule add https://github.com/iabudiab/HTMLKit.git

2- Open the HTMLKit folder and drag'n'drop the HTMLKit.xcodeproj into the Project Navigator in Xcode to add it as a sub-project.

3- In the General panel of your target add HTMLKit.framework under the Embedded Binaries

Parsing

Parsing Documents

Given some HTML content, you can parse it either via the HTMLParser or instatiate a HTMLDocument directly:

NSString *htmlString = @"<div><h1>HTMLKit</h1><p>Hello there!</p></div>";
// Via parser
HTMLParser *parser = [[HTMLParser alloc] initWithString:htmlString];
HTMLDocument *document = [parser parseDocument];
// Via static initializer
HTMLDocument *document = [HTMLDocument documentWithString:htmlString];

Parsing Fragments

You can also prase HTML content as a document fragment with a specified context element:

NSString *htmlString = @"<div><h1>HTMLKit</h1><p>Hello there!</p></div>";
HTMLParser *parser = [[HTMLParser alloc] initWithString: htmlString];
HTMLElement *tableContext = [[HTMLElement alloc] initWithTagName:@"table"];
NSArray *nodes = [parser parseFragmentWithContextElement:tableContext];
for (HTMLNode *node in nodes) {
NSLog(@"%@", node.outerHTML);
}
// The same parser instance can be reusued:
HTMLElement *bodyContext = [[HTMLElement alloc] initWithTagName:@"body"];
nodes = [parser parseFragmentWithContextElement:bodyContext];

The DOM

The DOM tree can be manipulated in several ways, here are just a few:

  • Create new elements and assign attributes
HTMLElement *description = [[HTMLElement alloc] initWithTagName:@"meta"attributes: @{@"name": @"description"}];
description[@"content"] = @"HTMLKit for iOS & OSX";
  • Append nodes to the document
HTMLElement *head = document.head;
[head appendNode:description];
HTMLElement *body = document.body;
NSArray *nodes = @[
[[HTMLElement alloc] initWithTagName:@"div"attributes: @{@"class": @"red"}],
[[HTMLElement alloc] initWithTagName:@"div"attributes: @{@"class": @"green"}],
[[HTMLElement alloc] initWithTagName:@"div"attributes: @{@"class": @"blue"}]
];
[body appendNodes:nodes];
  • Enumerate child elements and perform DOM editing
[body enumerateChildElementsUsingBlock:^(HTMLElement *element, NSUInteger idx, BOOL *stop) {
if ([element.tagName isEqualToString:@"div"]) {
HTMLElement *lorem = [[HTMLElement alloc] initWithTagName:@"p"];
lorem.textContent = [NSStringstringWithFormat:@"Lorem ipsum: %lu", (unsignedlong)idx];
[element appendNode:lorem];
}
}];
  • Remove nodes from the document
[body removeChildNodeAtIndex:1];
[head removeAllChildNodes];
[body.lastChild removeFromParentNode];
  • Manipulate the HTML directly
greenDiv.innerHTML = @"<ul><li>item 1<li>item 2";
  • Navigate to child and sibling nodes
HTMLNode *firstChild = body.firstChild;
HTMLNode *greenDiv = firstChild.nextSibling;
  • Iterate the DOM tree with custom filters
HTMLNodeFilterBlock *filter =[HTMLNodeFilterBlock filterWithBlock:^ HTMLNodeFilterValue (HTMLNode *node) {
if (node.childNodesCount != 1) {
return HTMLNodeFilterReject;
}
return HTMLNodeFilterAccept;
}];
for (HTMLElement *element in [body nodeIteratorWithShowOptions:HTMLNodeFilterShowElement filter:filter]) {
NSLog(@"%@", element.outerHTML);
}
  • Create and manipulate DOM Ranges
HTMLDocument *document = [HTMLDocument documentWithString:@"<div><h1>HTMLKit</h1><p id='foo'>Hello there!</p></div>"];
HTMLRange *range = [[HTMLRange alloc] initWithDocument:document];
HTMLNode *paragraph = [document querySelector:@"#foo"];
[range selectNode:paragraph];
[range extractContents];

CSS3 Selectors

All CSS3 Selectors are supported except for the pseudo-elements (::first-line, ::first-letter, ...etc.). You can use them the way you always have:

// Given the document:NSString *htmlString = @"<div><h1>HTMLKit</h1><p class='greeting'>Hello there!</p><p class='description'>This is a demo of HTMLKit</p></div>";
HTMLDocument *document = [HTMLDocument documentWithString: htmlString];
// Here are some of the supported selectorsNSArray *paragraphs = [document querySelectorAll:@"p"];
NSArray *paragraphsOrHeaders = [document querySelectorAll:@"p, h1"];
NSArray *hasClassAttribute = [document querySelectorAll:@"[class]"];
NSArray *greetings = [document querySelectorAll:@".greeting"];
NSArray *classNameStartsWith_de = [document querySelectorAll:@"[class^='de']"];
NSArray *hasAdjacentHeader = [document querySelectorAll:@"h1 + *"];
NSArray *hasSiblingHeader = [document querySelectorAll:@"h1 ~ *"];
NSArray *hasSiblingParagraph = [document querySelectorAll:@"p ~ *"];
NSArray *nonParagraphChildOfDiv = [document querySelectorAll:@"div :not(p)"];

HTMLKit 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:

NSArray *paragraphs = [document elementsMatchingSelector:typeSelector(@"p")];
NSArray *paragraphsOrHeaders = [document elementsMatchingSelector:anyOf(@[
typeSelector(@"p"), typeSelector(@"h1")
])
];
NSArray *hasClassAttribute = [document elementsMatchingSelector:hasAttributeSelector(@"class")];
NSArray *greetings = [document elementsMatchingSelector:classSelector(@"greeting")];
NSArray *classNameStartsWith_de = [document elementsMatchingSelector:attributeSelector(CSSAttributeSelectorBegins, @"class", @"de")];
NSArray *hasAdjacentHeader = [document elementsMatchingSelector:adjacentSiblingSelector(typeSelector(@"h1"))];
NSArray *hasSiblingHeader = [document elementsMatchingSelector:generalSiblingSelector(typeSelector(@"h1"))];
NSArray *hasSiblingParagraph = [document elementsMatchingSelector:generalSiblingSelector(typeSelector(@"p"))];
NSArray *nonParagraphChildOfDiv = [document elementsMatchingSelector:allOf(@[
childOfElementSelector(typeSelector(@"div")),
not(typeSelector(@"p"))
])
];

Here are more examples:

HTMLNode *firstDivElement = [document firstElementMatchingSelector:typeSelector(@"div")];
NSArray *secondChildOfDiv = [firstDivElement querySelectorAll:@":nth-child(2)"];
NSArray *secondOfType = [firstDivElement querySelectorAll:@":nth-of-type(2n)"];
secondChildOfDiv = [firstDivElement elementsMatchingSelector:nthChildSelector(CSSNthExpressionMake(0, 2))];
secondOfType = [firstDivElement elementsMatchingSelector:nthOfTypeSelector(CSSNthExpressionMake(2, 0))];
NSArray *notParagraphAndNotDiv = [firstDivElement querySelectorAll:@":not(p):not(div)"];
notParagraphAndNotDiv = [firstDivElement elementsMatchingSelector:allOf([
not(typeSelector(@"p")),
not(typeSelector(@"div"))
])
];

One 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:

CSSSelector *myAwesomeSelector = namedBlockSelector(@"myAwesomeSelector", ^BOOL (HTMLElement *element) {
return ![element.tagName isEqualToString:@"p"] && ![element.tagName isEqualToString:@"div"];
});
notParagraphAndNotDiv = [firstDivElement elementsMatchingSelector:myAwesomeSelector];

Change Log

See the CHANGELOG.md for more info.

License

HTMLKit is available under the MIT license. See the LICENSE file for more info.

About

An Objective-C framework for your everyday HTML needs.

Resources

Stars

0 stars

Watchers

3 watching

Forks

Releases

Packages

Used by

Contributors

Languages