An idea for an extension/link format I had a while ago and just wrote up quickly today:
Lots of web pages have sections and headings you want to link to directly, but you can’t because the author didn’t give proper anchoring IDs to nearby elements. I wanted to be able to do the equivalent of click on the link, then ctrl+f for "some phrase"
. With findlink, you can!
Examples: http://senocular.com/flash/tutorials/versions/#find(flash lite)
Senocular’s articles are nice because he put in lots of document structure. But for smaller sections than are in the outline at the top, you can’t link directly to things like the Flash Lite section.
http://www.kirupa.com/html5/sprite_sheet_animations_using_only_css.htm#find(actual implementation)
Wow, it jumps right to the implementation!
The code is pretty simple, it’s mostly just this:
tryFindScroll()
document.addEventListener(`DOMContentLoaded`, tryFindScroll)
document.addEventListener(`load`, tryFindScroll)
function tryFindScroll(){
if(location.hash){
const query = parse(decodeURIComponent(location.hash.substring(1)))
if(query){
scrollToMatchingTextNode(query)
}
}
}
`
Parses a URI fragment to find an occurrence of find(some search text)
`
function parse(fragment){
const match = fragment.match(/find\((.*)\)/i)
return match ? match[1] : undefined
}
`
Takes a find query string and looks for text in the page matching it.
If the query string is on the page, this function scrolls to the first match.
`
function scrollToMatchingTextNode(query){
const textNodes = document.evaluate(`//text()`, document)
for(let node of xpathIterator(textNodes)){
if(node.wholeText.match(new RegExp(query, `i`))){
`don't use Ranges yet because they're buggy`
/*const range = document.createRange()
range.selectNodeContents(node)
const textBounds = range.getBoundingClientRect()*/
document.body.scrollTop = /*textBounds.top*/ node.parentNode.getBoundingClientRect().top
return true
}
}
return false
}
`
I don't think that XPathResults conform to the ES6 for..of iteration protocol,
so this bridges the gap.
`
function* xpathIterator(xpathResult){
let next
while(next = xpathResult.iterateNext()) yield next
}
You can even install the extension in Chrome (once it appears in the web store in an hour or so…)