📜 ⬆️ ⬇️

16 really useful JavaScript solutions

© shamansir.wordpress.com

I present to you a set of functions that I have in a separate utils.js file - these are the functions that I use most often. They try to be cross-browser and tested on IE6 / 7, FF2 and Safari 2 and on a combat, complex system, in XHTML documents. Should, in theory, work, and on other, but not very old versions of browsers - I used the browser check only in exceptional cases. Some of them, of course, are simply dug up on the Internet (where it’s usually indicated) and borrowed because of openness, and most of it is constructed from many resources and ideas (and colleagues ’advice) in order to work with a bang - because often in different scripts do not take into account various subtleties that, nevertheless - on closer examination - turn out to be common :), well, be pretty readable.

The functions are divided thematically:
OOP - providing (or rather, emulation) the ability to use OOP principles in JavaScript
JS Object Model - using and extending JS embedded objects
Browser Definition - to use in those rare cases where it is nevertheless inevitable
Coordinates / Positioning - calculating coordinates and positioning objects - in view of the fact that this is often a rather tricky thing.
DOM - working with the document object model
AJAX - helper functions for AJAX - as this tool is often applicable
Logging - sometimes you just can't do without it

1. The first block is a set of three functions (two of which are empty), allowing to apply (emulate?) All three principles of OOP in JavaScript . Of the several options proposed on AJAXPath and on AJAXPatterns, I chose this one because of its simultaneous clarity and fast execution speed and modified it a bit, so that the separately declared properties are perceived as static constants.
function Class () {};
')
Class.prototype.construct = function () {};

Class.extend = function (def) {

var classDef = function () {
if (arguments [0]! == Class) {
this .construct.apply ( this , arguments);
}
};

var proto = new this (Class);
var superClass = this .prototype;

for ( var n in def) {
var item = def [n];
if (item instanceof Function) item. $ = superClass;
else classDef [n] = item;
proto [n] = item;
}

classDef.prototype = proto;
classDef.extend = this .extend;

return classDef;
}; * This source code was highlighted with Source Code Highlighter .


2. The following function is simple but elegant — useful in combination with the previous set — it creates a function reference to the method :
function createMethodReference ( object , methodName) {
return function () {
return object [methodName] .apply ( object , arguments);
};
} * This source code was highlighted with Source Code Highlighter .


Now you can, for example, do this:
var ScrollingHandler = Class.extend ({

construct:
function (elementId) {
this ._elementId = elementId;
this .assignListener ();
},

assignListener:
function () {
var scrollControlElem =
document .getElementById ( this ._elementId);
if (scrollControlElem) {
scrollControlElem.onscroll =
createMethodReference ( this , "_onElementScroll" );
}
},

_onElementScroll:
function (ev) {
ev = ev || window. event ;
alert ( "please stop scrolling,
I've already got an event: „ + ev);
}
});

var elmScrollHandler = new ScrollHandler ( 'SomeElmId' ); * This source code was highlighted with Source Code Highlighter .


An object of this class can be associated with a scrolling event of the element with the specified ID and perform something on this occasion.

JS object model


3. The following function clones any object along with all its properties:

function cloneObj (objToClone) {
var clone = [];
for (i in objToClone) {
clone [i] = objToClone [i];
}
return clone;
} * This source code was highlighted with Source Code Highlighter .

Use - the simplest to the impossibility:
var clonedObj = cloneObj (objToClone); * This source code was highlighted with Source Code Highlighter .


4. The converter of objects , the following function allows you to conveniently use all sorts of conditional (and pretending to be) constructions of the form
if (tablet.toLowerCase () in oc ([ 'cialis' , 'mevacor' , 'zocor' ])) {alert ('I will not!')}; * This source code was highlighted with Source Code Highlighter .

The code is borrowed from here .

function oc (a) {
var o = {};
for ( var i = 0; i <a.length; i ++) {
o [a [i]] = ”;
}
return o;
}
* This source code was highlighted with Source Code Highlighter .


For example, let’s take the situation when you first need to determine if an object is in a set of single objects, and then if it is not combined with another object in another set of objects. Assume that only singles with certain names are allowed to the party, or couples from the list with allowed combinations of names:
function isPersonAllowed (maleName, femaleName) {
var pairsAllowed = new Array ([ “John” , “Yoko” ],
[ "Bill" , "Monica" ], [ "Phil" , "Sue" ],
[ “Jason” , “Harrison” ], [ “Adam” , “Eve” ]);
var singlesAllowed = new Array ("Michael", "Pete", "John",
“Dave”, “Matthew”);
return (femaleName
? ([maleName, femaleName] in oc (pairsAllowed))
: (maleName in oc (singlesAllowed)));
}

alert (isPersonAllowed ("Jack")); // false
alert (isPersonAllowed ("Adam")); // false
alert (isPersonAllowed ("John")); // true
alert (isPersonAllowed ("Phil", "Marlo")); // false
alert (isPersonAllowed ("Jason", "Harrison")); // true
alert (isPersonAllowed ("Martin", "Luther")); // false * This source code was highlighted with Source Code Highlighter .


5. The function that allows you to create a hash at first seems a bit redundant: objects in JavaScript are the same hashes, but sometimes you need to set the value of a variable as the name of the property and then the Hash function comes to the rescue. (yes, yes, of course, there are built-in features, but maybe it's just a little more obvious - you can exclude this function from useful ones, if you want

function Hash ()
{
this .length = 0;
this .items = new Array ();
for ( var i = 0; i <arguments.length; i ++) {
this .items [arguments [i] [0]] = arguments [i] [1];
}
} * This source code was highlighted with Source Code Highlighter .


Access to the elements is done through the items property (by the way, you should add keys in a heavier version):
var Game = Class.extend ({
STG_STOP: 0,
STG_START: 1,
STG_LOADING: 2,
STG_MENU: 3,
STG_PROCESS: 4,

construct:
function () { this ._stage = Game.STG_LOADING; },

getStage:
function () { return this ._stage; }

});

var stateMap = new Hash (
[Game.STG_START, "start" ],
[Game.STG_LOADING, "loading" ],
[Game.STG_MENU, "menu" ],
[Game.STG_PROCESS, "process" ],
[Game.STG_STOP, “stopping” ]);

var someGame = new Game ();
alert ("You are in " + stateMap.items [someGame.getStage ()] + "stage!"); * This source code was highlighted with Source Code Highlighter .


6. Three other functions simply simplify and / or make some operations more obvious: getTime reduces the access to getting the current time by 11 characters, getTimeDelta allows you to find the gap in milliseconds between the time periods (or the specified time and the current time, in one-parameter format), and the last function extends the properties of the Number object so that with its NaN value one can get 0 little faster.
function getTime () {
return new Date (). getTime ();
}

function getTimeDelta (timeBegin, timeEnd) {
timeEnd = timeEnd || getTime ();
return timeEnd - timeBegin;
}

Number.prototype.NaN0 = function () { return isNaN ( this )? 0: this ; } * This source code was highlighted with Source Code Highlighter .


Browser Definition


7. A small object, called by the name of browsers whose properties are the essence of the conditions. This achieves a more readable (but not as scrupulous as it could be) definition of most types of browsers . This object was borrowed by me from a project in which I participated - and somehow got accustomed, but I think the true authors are still somewhere in the network, and the code is not that complicated and cumbersome to claim it strongly :) . In addition, it is certainly not ideally reliable (and some say it is not reliable at all), but so far on the browsers listed, it did not let me down even once :). If you are not satisfied with this state of affairs - you can use something similar with HowToCreate . And I repeat: I try to use this definition (as it is said, for example, by reference) “only if a specific bug is known in a specific browser and needs to be circumvented” . Also - it is easy to reassemble this object into one long condition, for a lower execution speed (see, again, the link )

var USER_DATA = {
Browser: {
KHTML: /Konqueror|KHTML/.test (navigator.userAgent) &&

! /Apple/.test (navigator.userAgent),
Safari: /KHTML/.test (navigator.userAgent) &&
/Apple/.test (navigator.userAgent),
Opera: !! window.opera,
MSIE: !! (window.attachEvent &&! Window.opera),
Gecko: /Gecko/.test (navigator.userAgent) &&
! / Konqueror | KHTML / .test (navigator.userAgent)
},
OS: {
Windows: navigator.platform.indexOf ( "Win" )> -1,
Mac: navigator.platform.indexOf ( "Mac" )> -1,
Linux: navigator.platform.indexOf ( "Linux" )> -1
}
} * This source code was highlighted with Source Code Highlighter .


Coordinates / Positioning


8. A set of functions that allow to obtain the coordinates of an element on the user's screen. If your document is static relative to the window and does not have scrollbars - it is better to use the getPosition function - this will be faster. In the opposite case, use getAlignedPosition - it takes into account the positions of the scrollbars. Just note: the top value of an element can be oriental if the element is at the top outside the window — in order to synchronize with the mouse cursor, sometimes it is necessary to reset the height in this case. The main script is borrowed from one blog , the Aligned version is the result of searching through the mixes and combining information from two articles (when a DOCTYPE IE is detected, it enters its own, somewhat unpredictable, mode). Also, this method is combined with obtaining positions from the source code guide for Drag'n'Drop . Please note: the NaN0 function from NaN0 6 is used here, you will need to add it to the script so that everything works as expected (thanks, Homer ).
var IS_IE = USER_DATA [ 'Browser' ] .MSIE;

function getPosition (e) {
var left = 0;
var top = 0;

while (e.offsetParent) {
left + = e.offsetLeft + (e.currentStyle?
(parseInt (e.currentStyle.borderLeftWidth)). NaN0 (): 0);
top + = e.offsetTop + (e.currentStyle?
(parseInt (e.currentStyle.borderTopWidth)). NaN0 (): 0);
e = e.offsetParent;
}

left + = e.offsetLeft + (e.currentStyle?
(parseInt (e.currentStyle.borderLeftWidth)). NaN0 (): 0);
top + = e.offsetTop + (e.currentStyle?
(parseInt (e.currentStyle.borderTopWidth)). NaN0 (): 0);

return {x: left, y: top};
}

function getAlignedPosition (e) {
var left = 0;
var top = 0;

while (e.offsetParent) {
left + = e.offsetLeft + (e.currentStyle?
(parseInt (e.currentStyle.borderLeftWidth)). NaN0 (): 0);
top + = e.offsetTop + (e.currentStyle?
(parseInt (e.currentStyle.borderTopWidth)). NaN0 (): 0);
e = e.offsetParent;
if (e.scrollLeft) {left - = e.scrollLeft; }
if (e.scrollTop) {top - = e.scrollTop; }
}

var docBody = document .documentElement?
document .documentElement: document .body;

left + = e.offsetLeft +
(e.currentStyle?
(parseInt (e.currentStyle.borderLeftWidth)). NaN0 ()
: 0) +
(IS_IE? (ParseInt (docBody.scrollLeft)). NaN0 (): 0) - (parseInt (docBody.clientLeft)). NaN0 ();
top + = e.offsetTop +
(e.currentStyle?
(parseInt (e.currentStyle.borderTopWidth)). NaN0 ()
: 0) +
(IS_IE? (ParseInt (docBody.scrollTop)). NaN0 (): 0) - (parseInt (docBody.clientTop)). NaN0 ();

return {x: left, y: top};
} * This source code was highlighted with Source Code Highlighter .


9. To determine the current coordinates of the mouse cursor and the displacement of the element relative to the cursor is easy if you use the appropriate functions:
function mouseCoords (ev) {
if (ev.pageX || ev.pageY) {
return {x: ev.pageX, y: ev.pageY};
}

var docBody = document .documentElement
? document .documentElement
: document .body;

return {
x: ev.clientX + docBody.scrollLeft - docBody.clientLeft,
y: ev.clientY + docBody.scrollTop - docBody.clientTop
};
}

function getMouseOffset (target, ev, aligned) {
ev = ev || window. event ;
if (aligned == null ) aligned = false ;

var docPos = aligned
? getAlignedPosition (target)
: getPosition (target);
var mousePos = mouseCoords (ev);

return {
x: mousePos.x - docPos.x,
y: mousePos.y - docPos.y
};
}
* This source code was highlighted with Source Code Highlighter .


The latter function can also be used in two modes due to the aligned attribute and is intended for convenient use in event handlers, for example:
function onMouseMove (elm, ev) {
var mouseOffset = getMouseOffset (elm, ev);
console.log ( "x:% d; y:% d" , mouseOffset.x, mouseOffset.y);
}
...
<div id = "someId" onmousemove = "onMouseMove (this, event);
return false; ” > </ div> This was the code highlighted with the Source Code Highlighter .


10. Determining the height of an element is sometimes more difficult than determining its other parameters, but these two functions will come to the rescue:
function findOffsetHeight (e) {
var res = 0;
while ((res == 0) && e.parentNode) {
e = e.parentNode;
res = e.offsetHeight;
}
return res;
}

function getOffsetHeight (e) {
return this .element.offsetHeight ||
this .element.style.pixelHeight ||
findOffsetHeight (e);
} * This source code was highlighted with Source Code Highlighter .


Dom


11. Sometimes you need to go recursively through the DOM tree , starting with a certain element and performing a certain function over each of the descendants, getting into the very depths. The DOM has a TreeWalker object, but it does not work in IE and is not always convenient / easy to use. The walkTree function allows walkTree to perform some other function on each of the elements and also allows you to send a certain data packet to it. The searchTree function differs from it in that it stops the passage through the tree at the first successful result and returns the result to the call point:
function walkTree (node, mapFunction, dataPackage) {
if (node ​​== null ) return ;
mapFunction (node, dataPackage);
for ( var i = 0; i <node.childNodes.length; i ++) {
walkTree (node.childNodes [i], mapFunction, dataPackage);
}
}

function searchTree (node, searchFunction, dataPackage) {
if (node ​​== null ) return ;
var funcResult = searchFunction (node, dataPackage);
if (funcResult) return funcResult;
for ( var i = 0; i <node.childNodes.length; i ++) {
var searchResult = searchTree (node.childNodes [i],
searchFunction, dataPackage);
if (searchResult) return searchResult;
}
} * This source code was highlighted with Source Code Highlighter .


The example uses the functions setElmAttr and getElmAttr , which will be discussed later in clause 13 . In essence, they do the same thing as getAttribute and setAttribute . You can find explanations of the used oc function in item 4 . In the first part of the example, the root element attribute “nodeType” is set to “root”, and all its descendants - in “child”. The second part also demonstrates the transfer of a data packet — when the first element with the attribute “class” equal to one of the names listed in the packet is found, the attribute “isTarget” is set to “true”.

var rootElement = document .getElementById ( 'rootElm' );

setElmAttr (rootElement, "nodeType" , "root" );
var childNodeFunc = function (node) {
if (node.nodeName && (node.nodeName! == '#text' )
&& (node.nodeName! == '#comment' )) {
setElmAttr (node, "nodeType" , "child" );
}
}
walkTree (rootElement, childNodeFunc);

var findTargetNode = function (node, classList) {
if ((node.nodeName && (node.nodeName! == '#text' )
&& (node.nodeName! == '#comment' )) &&
(getElmAttr (node, "class" ) in oc (classList))) {
return node;
}
}
var targetNode = searchTree (rootElement, findTargetNode,
[ 'headingClass' , 'footerClass' , 'tableClass' ]);
setElmAttr (targetNode, “isTarget”, true ); * This source code was highlighted with Source Code Highlighter .


NB! (be careful with the use of these functions and try to avoid their too frequent call (more than once per second) even on the average branching tree - they can devour a lot of resources. Or at least call them in the background via setTimeout )

12. Removing nodes is sometimes a necessary task. Sometimes you need to remove the node itself, and sometimes - only its descendants. The removeChildrenRecursively function recursively deletes all descendants of the specified node, without affecting, of course, itself. The function removeElementById , as the name says, deletes a node by its id — for all the simplicity of the task, the method is relatively tricky:
function removeChildrenRecursively (node)
{
if (! node) return ;
while (node.hasChildNodes ()) {
removeChildrenRecursively (node.firstChild);
node.removeChild (node.firstChild);
}
}

function removeElementById (nodeId) {
document .getElementById (nodeId) .parentNode.removeChild (
document .getElementById (nodeId));
}
* This source code was highlighted with Source Code Highlighter .


13. It would seem that the elementary task of working with the attributes of an element sometimes raises absolutely unexpected problems: for example, IE throws an exception when trying to access the height / width attributes of a table element, while Safari has a different way to access attributes with namespaces. The following functions bypass all the problems I have encountered without much damage to the execution speed (of course, in standard cases it is better to use the built-in functions):
var IS_SAFARI = USER_DATA [ 'Browser' ]. Safari;

function getElmAttr (elm, attrName, ns) {
// IE6 fails getAttribute when used on table element
var elmValue = null ;
try {
elmValue = (elm.getAttribute
? elm.getAttribute ((ns? (ns + NS_SYMB): ”)
+ attrName): null );
} catch (e) { return null ; }
if (! elmValue && IS_SAFARI) {
elmValue = (elm.getAttributeNS
? elm.getAttributeNS (ns, attrName)
: null );
}
return elmValue;
}

function setElmAttr (elm, attrName, value, ns) {
if (! IS_SAFARI ||! ns) {
return (elm.setAttribute
? elm.setAttribute ((ns? (ns + NS_SYMB): ”)
+ attrName, value): null );
} else {
return (elm.setAttributeNS
? elm.setAttributeNS (ns, attrName, value)
: null );
}
}

function remElmAttr (elm, attrName, ns) {
if (! IS_SAFARI ||! ns) {
return (elm.removeAttribute
? elm.removeAttribute ((ns? (ns + NS_SYMB): ”)
+ attrName): null );
} else {
return (elm.removeAttributeNS
? elm.removeAttributeNS (ns, attrName)
: null );
}
} * This source code was highlighted with Source Code Highlighter .


Logging


14. The function below to help with logging is very simple, add the <div id="LOG_DIV"></div> element to the right place in the document, set the required height to it, and the information + will be reset to it and scrolled:
function LOG (informerName, text) {
var logElement = document .getElementById ( 'LOG_DIV' );
if (logElement) {
logElement.appendChild ( document .createTextNode (
informerName + ':' + text));
logElement.appendChild ( document .createElement ( 'br' ));
logElement.scrollTop + = 50;
}
} * This source code was highlighted with Source Code Highlighter .


15. In the wonderful Firebug plugin for the Firefox browser there is a wonderful console in which you can perform logging with many features. However, if you are debugging in parallel the code in other browsers, references to it can cause errors. In order not to clear the code from the logs every time, you can use the following stub:
var Console = Class.extend ({
// the stub
// if not - just pass all calls
construct: function () {},
log: function () {},
info: function () {},
warn: function () {},
error: function () {}
});

if (! window.console) {
console = new Console ();
} * This source code was highlighted with Source Code Highlighter .


UPD: Corrected links to Source Code Highlighter, thanks.

Source: https://habr.com/ru/post/28325/


All Articles