Skip to content

Instantly share code, notes, and snippets.

@intuited
Created July 25, 2010 00:03
Show Gist options
  • Save intuited/489116 to your computer and use it in GitHub Desktop.
Save intuited/489116 to your computer and use it in GitHub Desktop.

followPreviousID

Moves backward and upward in the DOM to find the closest node with an ID attribute (i.e. one which can be targeted in the URL fragment). If it finds one, updates the document URL to include that fragment.

The main function is parameterized to allow the maximum search depth to be limited. The depth defaults to 2; passing -1 will give an unlimited search depth.

Useful for bookmarklets.

Tested on Chrome and Chrome only.

Doesn't rely on any external frameworks.

Started life as gist #489116.

TODO

  • Create a Makefile of some variety that spits out minified text suitable for a bookmarklet
  • Write some tests
/* Enables all of "The Good Parts"
* except for "use strict" and Strict white space.
* I turned off Strict white space
* mostly because it doesn't allow one-line if statements.
*/
/*jslint browser: true, onevar: true, undef: true, nomen: true, eqeqeq: true, plusplus: true, bitwise: true, regexp: true, newcap: true, immed: true */
/*global window */
(function(maxdepth) {
var getPreviousID, getSelectionNode, makeURI, followPreviousID;
getPreviousID = function(node, maxdepth) {
/* Iterate backwards and upwards through the DOM from `node`.
* Previous siblings will be recursed to a depth of `maxdepth`,
* or completely for `maxdepth < 0`.
*/
if (maxdepth === undefined) { maxdepth = -1; }
var getDescendantID, previous, id;
getDescendantID = function(node, depth) {
var child, descendant_id;
if (depth === maxdepth) { return false; }
child = node.firstChild;
while (child) {
if (child.id) { return child.id; }
if (child.name) { return child.name; }
descendant_id = getDescendantID(child, depth + 1);
if (descendant_id) { return descendant_id; }
child = child.nextSibling;
}
return false;
};
previous = null;
while (node) {
if (node.id) { return node.id; }
if (node.name) { return node.name; }
/* Only recurse into siblings; children have already been searched. */
if (previous && (id = getDescendantID(node, 0))) { return id; }
node = (previous = node.previousSibling) || node.parentNode;
}
return false;
};
getSelectionNode = function() { return window.getSelection().baseNode; };
makeURI = function(base, id) {
var hashIndex;
hashIndex = base.lastIndexOf('#');
if (hashIndex > -1) { base = base.slice(0, hashIndex); }
return base + '#' + id;
};
followPreviousID = function(maxdepth) {
/* Iterate backward and upward through the DOM,
* looking for a node with an ID attribute.
* `maxdepth` determines the depth to which previous nodes are searched.
* By default this will be a depth of 2.
* Passing 0 will prevent searching in subnodes.
* Passing -1 will result in a full traversal of the previous DOM nodes.
*/
if (maxdepth === undefined) { maxdepth = 2; }
var id;
id = getPreviousID(getSelectionNode(), maxdepth);
if (id) {
document.location.href = makeURI(document.location.href, id);
}
};
followPreviousID(maxdepth);
}());
@intuited
Copy link
Author

You should be able to just copy-paste it into the location of a new bookmark, after "javascript:". To use it, select some text and then click on the bookmarklet. If there's an element with an ID attribute somewhere before the selected text, that ID will become the window's new location.

@intuited
Copy link
Author

Updated so that it will recurse into earlier nodes. Also eliminated use of global variables.

@intuited
Copy link
Author

Changed again; now it will only recurse to a depth of 2. I think this is usually what I want: it will be pretty much guaranteed to find IDs in headings but not necessarily in sub-paragraphs. It's all pretty vague and based on the structure of the individual document. Passing a value of -1 in the function call at the end will cause it to do a thorough search of the document for the absolute nearest previous ID. Passing 0 will cause it to ignore IDs in any subnodes..

@intuited
Copy link
Author

I also gave it a more sensible name.

@intuited
Copy link
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment