|
|
(65 intermediate revisions by 15 users not shown) |
Line 1: |
Line 1: |
| __TOC__
| | #REDIRECT [[:Category:Coding Tips]] |
| | |
| == Shortcut to document.getElementById ==
| |
| | |
| function $(id) {
| |
| return document.getElementById(id);
| |
| }
| |
| | |
| Example usage:
| |
| | |
| $("header").innerHTML = "Halloa!";
| |
| | |
| | |
| == XPath helper ==
| |
| | |
| Run a particular [[XPath]] expression <code>p</code> against the context node <code>context</code> (or the document, if not provided).
| |
| | |
| Returns the results as an array.
| |
| | |
| function $x(p, context) {
| |
| if (!context) context = document;
| |
| var i, arr = [], xpr = document.evaluate(p, context, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
| |
| for (i = 0; item = xpr.snapshotItem(i); i++) arr.push(item);
| |
| return arr;
| |
| }
| |
| | |
| Example usage (with [[Coding_tips#array.forEach|Array.forEach]]):
| |
| | |
| var paragraphs = $x("//p");
| |
| paragraphs.forEach(function(paragraph) { // Loop over every paragraph
| |
| paragraph.innerHTML = "Halloa!";
| |
| });
| |
| | |
| '''Note:''' When you specify a context node, you need to use a [[XPath#Relative_paths|relative XPath expression]].
| |
| | |
| == Get elements by CSS selector ==
| |
| | |
| Lets you run an XPath using the concise CSS style selectors such as .class and #id, it also adds a much needed method of comparing the ends of strings, $=.
| |
| | |
| function $$(xpath,root) {
| |
| xpath=xpath.replace(/((^|\|)\s*)([^/|\s]+)/g,'$2.//$3').
| |
| replace(/\.([\w-]+)(?!([^\]]*]))/g,'[@class="$1" or @class$=" $1" or @class^="$1 " or @class~=" $1 "]').
| |
| replace(/#([\w-]+)/g,'[@id="$1"]').
| |
| replace(/\/\[/g,'/*[');
| |
| str='(@\\w+|"[^"]*"|\'[^\']*\')'
| |
| xpath=xpath.replace(new RegExp(str+'\\s*~=\\s*'+str,'g'),'contains($1,$2)').
| |
| replace(new RegExp(str+'\\s*\\^=\\s*'+str,'g'),'starts-with($1,$2)').
| |
| replace(new RegExp(str+'\\s*\\$=\\s*'+str,'g'),'substring($1,string-length($1)-string-length($2)+1)=$2');
| |
| var got=document.evaluate(xpath,root||document,null,null,null), result=[];
| |
| while(next=got.iterateNext()) result.push(next);
| |
| return result;
| |
| }
| |
| | |
| | |
| Example usage:
| |
| $$('#title')[0].innerHTML='Greased';
| |
| $$('a[@href $= "user.js"]').forEach(function (a) {
| |
| a.innerHTML='check it out a script';
| |
| }
| |
| $$('a[@href ^= "http"]').forEach(function (a) {
| |
| a.innerHTML += ' (external)';
| |
| }
| |
| | |
| == Conditional logging ==
| |
| | |
| Used to easily toggle sending debug messages to the console. Passes all arguments on to [http://www.getfirebug.com/logging.html console.log], but only if <code>console</code> is defined (for backward compatibility) and <code>DEBUG</code> is <code>true</code>.
| |
| | |
| Code and example usage:
| |
| | |
| const DEBUG = true;
| |
|
| |
| var links = document.links;
| |
| debug("Links: %o", links);
| |
|
| |
| function debug() {
| |
| if (DEBUG && console) {
| |
| console.log.apply(this, arguments);
| |
| }
| |
| }
| |
| | |
| ======Another way...======
| |
| ...to do it would be this:
| |
| | |
| const DEBUG = 1;
| |
|
| |
| console =
| |
| {
| |
| log : function (text) { if( DEBUG ) unsafeWindow.console.log( text ); },
| |
| info : function (text) { if( DEBUG ) unsafeWindow.console.info( text ); },
| |
| warn : function (text) { if( DEBUG ) unsafeWindow.console.warn( text ); },
| |
| error : function (text) { if( DEBUG ) unsafeWindow.console.error( text ); }
| |
| }
| |
| | |
| which allows you to just support more functions from the firebug console if you want and use it with unchanged syntax.
| |
| | |
| == DOM node manipulation ==
| |
| | |
| === Use .innerHTML to create DOM structure ===
| |
| The non W3 standard setter method [http://developer.mozilla.org/en/docs/DOM:element.innerHTML <code>.innerHTML</code>] can be used to concisely create DOM structure in trivial userscripts. The following is a complete branding script to show that Greasemonkey has run on a host.
| |
| | |
| host = document.location.host;
| |
| dummyDiv = document.createElement('div');
| |
| dummyDiv.innerHTML = '<div><span style="color: red">Greased: ' + host + '</span></div>';
| |
| document.body.insertBefore(dummyDiv.firstChild, document.body.firstChild);
| |
| | |
| Note above that <code>dummyDiv</code> is only needed as a holder for its <code>.firstChild</code>.
| |
| | |
| Here is a helper function that reuses the <code>dummyDiv</code>:
| |
| | |
| function firstNodeOf(html){
| |
| firstNodeOf.dummyDiv.innerHTML = html;
| |
| return firstNodeOf.dummyDiv.firstChild;
| |
| }
| |
| firstNodeOf.dummyDiv = document.createElement('div');
| |
| | |
| With this helper, one can write the following trivial script:
| |
| | |
| document.body.appendChild(firstNodeOf('<div><span style="color: red">END OF PAGE</span></div>');
| |
| | |
| '''STRONGLY NOTE:''' <code>.innerHTML</code> seems to be sensitive to <code>document.contentType</code>. When the type is <code>text/plain</code> the <code>.innerHTML</code> setter does not parse its argument into DOM nodes, but instead returns #text nodes. The setter seems to work fine for types such as <code>text/html</code> or <code>application/xhtml+xml</code> but where and how it works is undocumented.
| |
| | |
| '''Advantages:''' Concise and and probably more efficient than any hand coded approach.
| |
| | |
| '''Disadvantages:''' Other than the <code>text/plain</code> probem, <code>.innerHTML</code> is not a W3 standard and many people don't like non-standard code.
| |
| | |
| '''Upshot:''' Until [http://developer.mozilla.org/en/docs/E4X ECMAScript for XML (E4X)] becomes availble in user scripts, this is a useful hack for non <code>text/plain</code> pages.
| |
| ==== Notes ====
| |
| * <code>.innerHTML</code> [http://developer.mozilla.org/en/docs/DOM:element.innerHTML#Notes cannot be used to create parts of a <code>TABLE</code>]. E.g.
| |
| | |
| tab = firstNodeOf('<table><tr><td> ... </td></tr></table>'); // WORKS
| |
| tr = firstNodeOf('<tr><td> ... </td></tr>'); // DOESN'T WORK
| |
| td = firstNodeOf('<td> ... </td>'); // DOESN'T WORK
| |
| td.innerHTML = ' ... '; // WORKS
| |
| | |
| So, if a script builds a table from a HTML string, it has to be done as a whole rather than in parts.
| |
| | |
| === Build a DOM node with attributes ===
| |
| | |
| Creates a new DOM node with the given attributes.
| |
| | |
| function createElement(type, attributes){
| |
| var node = document.createElement(type);
| |
| for (var attr in attributes) if (attributes.hasOwnProperty(attr)){
| |
| node.setAttribute(attr, attributes[attr]);
| |
| }
| |
| return node;
| |
| }
| |
| Example usage:
| |
| | |
| link = createElement('link', {rel: 'stylesheet', type: 'text/css', href: basedir + 'style.css'});
| |
| | |
| === Remove DOM node ===
| |
| | |
| function remove(element) {
| |
| element.parentNode.removeChild(element);
| |
| }
| |
| | |
| === Insert node after node ===
| |
| | |
| function insertAfter(newNode, node) {
| |
| return node.parentNode.insertBefore(newNode, node.nextSibling);
| |
| }
| |
| | |
| This works because even if <code>node</code> is the last node, <code>nextSibling</code> returns <code>null</code> so <code>insertBefore</code> puts the new node at the end.
| |
| | |
| Example usage:
| |
| | |
| var link = document.getElementById("the_link");
| |
| var icon = document.createElement("img");
| |
| icon.src = "…";
| |
| insertAfter(icon, link);
| |
| | |
| === Hijacking browser properties ===
| |
| | |
| Sometimes you want to cook your own browser constants, for instance change the value of navigator.userAgent. Getters are good for that:
| |
| | |
| var real = window.navigator.userAgent;
| |
| var lie = function() { return real + " Macintosh"; };
| |
| unsafeWindow.navigator.__defineGetter__("userAgent", lie);
| |
| | |
| === Extending the DOM with missing functions ===
| |
| | |
| Other times you might want to emulate proprietary functionality of another browser, for instance when a site uses such features:
| |
| | |
| var getter = function() { return this.textContent; };
| |
| var setter = function(t) { return this.textContent = t; };
| |
| unsafeWindow.HTMLElement.prototype.__defineGetter__("innerText", getter);
| |
| unsafeWindow.HTMLElement.prototype.__defineSetter__("innerText", setter);
| |
| | |
| === Advanced createElement for creating hierarchies of elements ===
| |
| Creates an element with attributes as well as child elements with their own attributes and children. Function should be called with arguments in the form of the following hash (note that "child1", "child2" should be hashes of the same structure):
| |
| createEl({n: nodename, a: {attr1: val, attr2: val}, c: [child1, child2], evl: {type: eventlistener_type, f: eventlistener_function, bubble: bool}}, appendTo)
| |
| | |
| | |
| function createEl(elObj, parent) {
| |
| var el;
| |
| if (typeof elObj == 'string') {
| |
| el = document.createTextNode(elObj);
| |
| }
| |
| else {
| |
| el = document.createElement(elObj.n);
| |
| if (el
| |