

Obsidian Bookmarklet to clip pages
source link: https://gist.github.com/kepano/90c05f162c37cf730abb8ff027987ca3
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

By @kepano
Support my work at buymeacoffee.com/kepano
You can find a demo of this bookmarklet on YouTube
Installation
Create a new bookmark in your browser, then copy/paste the minified code below into the URL field.
You can customize the output using the optional variables at the top, and the template at the bottom. The default template is designed for use with the Dataview plugin. If you make changes I recommend using Bookmarklet Maker to minify and URI encode the bookmarklet.
Usage
By default, clicking the bookmarklet creates a new Obsidian file from the main body of the article (similar to Readability view). Alternatively you can choose to create a file from a selection, by either selecting all (CMD+A
), or just a portion of the page.
Troubleshooting
This bookmarklet may not work on all websites. If you run into issues, you can also try the MarkDownload browser extension which provides similar functionality. You can troubleshoot issues by opening the Developer Console in your browser and checking if any errors appear when you click the bookmarklet. The most common error is that a website or the browser itself is blocking third party code execution. Unfortunately there is no good solve for that yet.
javascript: Promise.all([import('https://unpkg.com/[email protected]?module'), import('https://unpkg.com/@tehshrike/[email protected]'), ]).then(async ([{ default: Turndown }, { default: Readability }]) => {
/* Optional vault name */ const vault = "";
/* Optional folder name such as "Clippings/" */ const folder = "";
/* Optional tags */ const tags = "#clippings";
function getSelectionHtml() { var html = ""; if (typeof window.getSelection != "undefined") { var sel = window.getSelection(); if (sel.rangeCount) { var container = document.createElement("div"); for (var i = 0, len = sel.rangeCount; i < len; ++i) { container.appendChild(sel.getRangeAt(i).cloneContents()); } html = container.innerHTML; } } else if (typeof document.selection != "undefined") { if (document.selection.type == "Text") { html = document.selection.createRange().htmlText; } } return html; }
const selection = getSelectionHtml();
const { title, byline, content } = new Readability(document.cloneNode(true)).parse();
function getFileName(fileName) { var userAgent = window.navigator.userAgent, platform = window.navigator.platform, windowsPlatforms = ['Win32', 'Win64', 'Windows', 'WinCE'];
if (windowsPlatforms.indexOf(platform) !== -1) { fileName = fileName.replace(':', '').replace(/[/\\?%*|"<>]/g, '-'); } else { fileName = fileName.replace(':', '').replace(/\//g, '-').replace(/\\/g, '-'); } return fileName; } const fileName = getFileName(title);
if (selection) { var markdownify = selection; } else { var markdownify = content; }
if (vault) { var vaultName = '&vault=' + encodeURIComponent(`${vault}`); } else { var vaultName = ''; }
const markdownBody = new Turndown({ headingStyle: 'atx', hr: '---', bulletListMarker: '-', codeBlockStyle: 'fenced', emDelimiter: '*', }).turndown(markdownify);
var date = new Date();
function convertDate(date) { var yyyy = date.getFullYear().toString(); var mm = (date.getMonth()+1).toString(); var dd = date.getDate().toString(); var mmChars = mm.split(''); var ddChars = dd.split(''); return yyyy + '-' + (mmChars[1]?mm:"0"+mmChars[0]) + '-' + (ddChars[1]?dd:"0"+ddChars[0]); }
const today = convertDate(date);
const fileContent = "author:: " + byline + "\n" + "source:: [" + title + "](" + document.URL + ")\n" + "clipped:: [[" + today + "]]\n" + "published:: \n\n" + tags + "\n\n" + markdownBody ; document.location.href = "obsidian://new?" + "file=" + encodeURIComponent(folder + fileName) + "&content=" + encodeURIComponent(fileContent) + vaultName ; })
javascript:(function()%7Bjavascript%3A%20Promise.all(%5Bimport('https%3A%2F%2Funpkg.com%2Fturndown%406.0.0%3Fmodule')%2C%20import('https%3A%2F%2Funpkg.com%2F%40tehshrike%2Freadability%400.2.0')%2C%20%5D).then(async%20(%5B%7B%0A%20%20%20%20default%3A%20Turndown%0A%7D%2C%20%7B%0A%20%20%20%20default%3A%20Readability%0A%7D%5D)%20%3D%3E%20%7B%0A%0A%20%20%2F*%20Optional%20vault%20name%20*%2F%0A%20%20const%20vault%20%3D%20%22%22%3B%0A%0A%20%20%2F*%20Optional%20folder%20name%20such%20as%20%22Clippings%2F%22%20*%2F%0A%20%20const%20folder%20%3D%20%22%22%3B%0A%0A%20%20%2F*%20Optional%20tags%20%20*%2F%0A%20%20const%20tags%20%3D%20%22%23clippings%22%3B%0A%0A%20%20function%20getSelectionHtml()%20%7B%0A%20%20%20%20var%20html%20%3D%20%22%22%3B%0A%20%20%20%20if%20(typeof%20window.getSelection%20!%3D%20%22undefined%22)%20%7B%0A%20%20%20%20%20%20%20%20var%20sel%20%3D%20window.getSelection()%3B%0A%20%20%20%20%20%20%20%20if%20(sel.rangeCount)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20var%20container%20%3D%20document.createElement(%22div%22)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20for%20(var%20i%20%3D%200%2C%20len%20%3D%20sel.rangeCount%3B%20i%20%3C%20len%3B%20%2B%2Bi)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20container.appendChild(sel.getRangeAt(i).cloneContents())%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20html%20%3D%20container.innerHTML%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%20else%20if%20(typeof%20document.selection%20!%3D%20%22undefined%22)%20%7B%0A%20%20%20%20%20%20%20%20if%20(document.selection.type%20%3D%3D%20%22Text%22)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20html%20%3D%20document.selection.createRange().htmlText%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%0A%20%20%20%20return%20html%3B%0A%20%20%7D%0A%0A%20%20const%20selection%20%3D%20getSelectionHtml()%3B%0A%0A%20%20const%20%7B%0A%20%20%20%20%20%20title%2C%0A%20%20%20%20%20%20byline%2C%0A%20%20%20%20%20%20content%0A%20%20%7D%20%3D%20new%20Readability(document.cloneNode(true)).parse()%3B%0A%0A%20%20function%20getFileName(fileName)%20%7B%0A%20%20%20%20var%20userAgent%20%3D%20window.navigator.userAgent%2C%0A%20%20%20%20%20%20%20%20platform%20%3D%20window.navigator.platform%2C%0A%20%20%20%20%20%20%20%20windowsPlatforms%20%3D%20%5B'Win32'%2C%20'Win64'%2C%20'Windows'%2C%20'WinCE'%5D%3B%0A%0A%20%20%20%20if%20(windowsPlatforms.indexOf(platform)%20!%3D%3D%20-1)%20%7B%0A%20%20%20%20%20%20fileName%20%3D%20fileName.replace('%3A'%2C%20'').replace(%2F%5B%2F%5C%5C%3F%25*%7C%22%3C%3E%5D%2Fg%2C%20'-')%3B%0A%20%20%20%20%7D%20else%20%7B%0A%20%20%20%20%20%20fileName%20%3D%20fileName.replace('%3A'%2C%20'').replace(%2F%5C%2F%2Fg%2C%20'-').replace(%2F%5C%5C%2Fg%2C%20'-')%3B%0A%20%20%20%20%7D%0A%20%20%20%20return%20fileName%3B%0A%20%20%7D%0A%20%20const%20fileName%20%3D%20getFileName(title)%3B%0A%0A%20%20if%20(selection)%20%7B%0A%20%20%20%20%20%20var%20markdownify%20%3D%20selection%3B%0A%20%20%7D%20else%20%7B%0A%20%20%20%20%20%20var%20markdownify%20%3D%20content%3B%0A%20%20%7D%0A%0A%20%20if%20(vault)%20%7B%0A%20%20%20%20%20%20var%20vaultName%20%3D%20'%26vault%3D'%20%2B%20encodeURIComponent(%60%24%7Bvault%7D%60)%3B%0A%20%20%7D%20else%20%7B%0A%20%20%20%20%20%20var%20vaultName%20%3D%20''%3B%0A%20%20%7D%0A%0A%20%20const%20markdownBody%20%3D%20new%20Turndown(%7B%0A%20%20%20%20%20%20headingStyle%3A%20'atx'%2C%0A%20%20%20%20%20%20hr%3A%20'---'%2C%0A%20%20%20%20%20%20bulletListMarker%3A%20'-'%2C%0A%20%20%20%20%20%20codeBlockStyle%3A%20'fenced'%2C%0A%20%20%20%20%20%20emDelimiter%3A%20'*'%2C%0A%20%20%7D).turndown(markdownify)%3B%0A%0A%20%20var%20date%20%3D%20new%20Date()%3B%0A%0A%20%20function%20convertDate(date)%20%7B%0A%20%20%20%20var%20yyyy%20%3D%20date.getFullYear().toString()%3B%0A%20%20%20%20var%20mm%20%3D%20(date.getMonth()%2B1).toString()%3B%0A%20%20%20%20var%20dd%20%20%3D%20date.getDate().toString()%3B%0A%20%20%20%20var%20mmChars%20%3D%20mm.split('')%3B%0A%20%20%20%20var%20ddChars%20%3D%20dd.split('')%3B%0A%20%20%20%20return%20yyyy%20%2B%20'-'%20%2B%20(mmChars%5B1%5D%3Fmm%3A%220%22%2BmmChars%5B0%5D)%20%2B%20'-'%20%2B%20(ddChars%5B1%5D%3Fdd%3A%220%22%2BddChars%5B0%5D)%3B%0A%20%20%7D%0A%0A%20%20const%20today%20%3D%20convertDate(date)%3B%0A%0A%20%20const%20fileContent%20%3D%20%0A%20%20%20%20%20%20%22author%3A%3A%20%22%20%2B%20byline%20%2B%20%22%5Cn%22%0A%20%20%20%20%20%20%2B%20%22source%3A%3A%20%5B%22%20%2B%20title%20%2B%20%22%5D(%22%20%2B%20document.URL%20%2B%20%22)%5Cn%22%0A%20%20%20%20%20%20%2B%20%22clipped%3A%3A%20%5B%5B%22%20%2B%20today%20%2B%20%22%5D%5D%5Cn%22%0A%20%20%20%20%20%20%2B%20%22published%3A%3A%20%5Cn%5Cn%22%20%0A%20%20%20%20%20%20%2B%20tags%20%2B%20%22%5Cn%5Cn%22%0A%20%20%20%20%20%20%2B%20markdownBody%20%3B%0A%20%20%0A%20%20document.location.href%20%3D%20%22obsidian%3A%2F%2Fnew%3F%22%0A%20%20%20%20%2B%20%22file%3D%22%20%2B%20encodeURIComponent(folder%20%2B%20fileName)%0A%20%20%20%20%2B%20%22%26content%3D%22%20%2B%20encodeURIComponent(fileContent)%0A%20%20%20%20%2B%20vaultName%20%3B%0A%7D)%7D)()%3B
Copy link
gkzsolt commented 14 days ago
Hi
This would be very nice, but I get "Uncaught (in promise) TypeError: Failed to fetch dynamically imported module: https://unpkg.com/[email protected]?module" when clicking the bookmarklet.
Hi
This would be very nice, but I get "Uncaught (in promise) TypeError: Failed to fetch dynamically imported module: https://unpkg.com/[email protected]?module" when clicking the bookmarklet.
This issue seems to be triggered when using the bookmarklet inside GitHub:
Refused to load the script 'https://unpkg.com/[email protected]?module' because it violates the following Content Security Policy directive: "script-src github.githubassets.com". Note that 'script-src-elem' was not explicitly set, so 'script-src' is used as a fallback.
I've tested it in another page, like https://obsidianroundup.org/ and it's working.
@kepano: When constructing the obsidian://
link, don’t you think it’d be better to also URI-encode folder & name? People can have odd file & folder names …
@kepano: Here’s my slightly modified version for us YAML aficionados, would be glad if you’d include that in your collection:
javascript: Promise.all([import('https://unpkg.com/[email protected]?module'), import('https://unpkg.com/@tehshrike/[email protected]'), ]).then(async ([{ default: Turndown }, { default: Readability }]) => { /* Optional vault name */ const vault = ""; /* Optional folder name such as "Clippings/" */ const folder = ""; /* Optional tags */ const tags = "clippings"; function getSelectionHtml() { var html = ""; if (typeof window.getSelection != "undefined") { var sel = window.getSelection(); if (sel.rangeCount) { var container = document.createElement("div"); for (var i = 0, len = sel.rangeCount; i < len; ++i) { container.appendChild(sel.getRangeAt(i).cloneContents()); } html = container.innerHTML; } } else if (typeof document.selection != "undefined") { if (document.selection.type == "Text") { html = document.selection.createRange().htmlText; } } return html; } const selection = getSelectionHtml(); const { title, byline, content } = new Readability(document.cloneNode(true)).parse(); function getFileName(fileName) { var userAgent = window.navigator.userAgent, platform = window.navigator.platform, windowsPlatforms = ['Win32', 'Win64', 'Windows', 'WinCE']; if (windowsPlatforms.indexOf(platform) !== -1) { fileName = fileName.replace(':', '').replace(/[/\\?%*|"<>]/g, '-'); } else { fileName = fileName.replace(':', '').replace(/\//g, '-').replace(/\\/g, '-'); } return fileName; } const fileName = getFileName(title); if (selection) { var markdownify = selection; } else { var markdownify = content; } if (vault) { var vaultName = '&vault=' + encodeURIComponent(`${vault}`); } else { var vaultName = ''; } const markdownBody = new Turndown({ headingStyle: 'atx', hr: '---', bulletListMarker: '-', codeBlockStyle: 'fenced', emDelimiter: '*', }).turndown(markdownify); var date = new Date(); function convertDate(date) { var yyyy = date.getFullYear().toString(); var mm = (date.getMonth()+1).toString(); var dd = date.getDate().toString(); var mmChars = mm.split(''); var ddChars = dd.split(''); return yyyy + '-' + (mmChars[1]?mm:"0"+mmChars[0]) + '-' + (ddChars[1]?dd:"0"+ddChars[0]); } const today = convertDate(date); const fileContent = "---\n" + "date: " + today + "\n" + "tags: [" + tags + "]\n" + "source: " + document.URL + "\n" + "author: " + byline + "\n" + "---\n\n" + markdownBody; document.location.href = "obsidian://new?" + "name=" + encodeURIComponent(folder + fileName) + "&content=" + encodeURIComponent(fileContent) + vaultName; })
@kepano: When constructing the
obsidian://
link, don’t you think it’d be better to also URI-encode folder & name? People can have odd file & folder names …
You're right! Updated the code with your suggestions.
Doesn't work on safari on macOS Monterey. Does it work on public versions of safari? Works on Firefox well enough. Thanks a lot for this!
Doesn't work on safari on macOS Monterey. Does it work on public versions of safari? Works on Firefox well enough. Thanks a lot for this!
I think Safari prefers if the JS has been minified and URI encoded. I have a minified version in the Gist, but if you choose to edit the variables or template you'll need to do that yourself. You can run the code through Bookmarket Maker to minify + create the bookmarklet for Safari.
I have no idea on javascript. Will open the tab in Firefox and manage for now. Given how many centuries notion took to develop a clipper for safari, I have learnt to push it around.
I have no idea on javascript. Will open the tab in Firefox and manage for now. Given how many centuries notion took to develop a clipper for safari, I have learnt to push it around.
This is working fine for me in Safari. I opened the minified js above as Raw and copied that.
@kepano thanks for the really useful bookmarklet! (and @Moonbase59 for the yaml version)
Is it maybe possible to get the date of publication as well? That would be super useful when you want to clip newspaper articles.
also, small improvement: for the URL-scheme, using name
creates a problem because notes are placed in the default location for new notes.
When you have save in the same folder as the current file
set as default location for new notes, and when you also set an optional folder at the top of the bookmarklet script, multiple consecutive clippings result in tons of unintended nested folders, as the "current file" (the clipping) will always be one level deeper than before.
Use file
instead of name
in the url scheme and this is fixed.
Use
file
instead ofname
in the url scheme and this is fixed.
Thanks, this change is made
Is it maybe possible to get the date of publication as well? That would be super useful when you want to clip newspaper articles.
I'm using Mozilla's readability library which unfortunately doesn't provide published date as an output. Would love to see this too, but don't have the time to explore adding this feature right now. Might be a good suggestion for the readability library overall as I could imagine other use cases for it.
Copy link
nimerino commented 7 days ago
@kepano: Here’s my slightly modified version for us YAML aficionados, would be glad if you’d include that in your collection:
Thank you so much @Moonbase59 for the YAML extension.
And @kepano, I take my hat off to you as always.
Copy link
bblais commented 3 days ago
How hard would it be to export/save all images as well, kind of like the "Save Entire Website" option in Chrome? I say this because, when I want to save a website in Obsidian I don't want to count on the website keeping all the images, and I want to be able to look at it offline. It's a pretty cool tool!
How hard would it be to export/save all images as well, kind of like the "Save Entire Website" option in Chrome?
You can do this using the MarkDownload extension
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK