Представляю вам набор функций, которые у меня лежат в отдельном файле utils.js - это функции, которые я использую чаще всего. Они стараются быть кроссбраузерными и проверены на IE6/7, FF2 и Safari 2 и на боевой, сложной системе, в XHTML документах. Должны, по идее, работать, и на других, но не очень старых версиях браузеров - проверку браузера я использовал только в исключительных случаях. Некоторая часть из них, конечно же, просто нарыта на просторах интернета (где - обычно указано) и заимствована ввиду открытости, а большая часть - сконструирована из многих ресурсов и своих идей (и советов коллег), дабы работать на ура - поскольку часто в разных скриптах не учитываются разные тонкости, которые, тем не менее - при ближайшем рассмотрении - оказываются общностями :), ну и быть довольно читабельными.
Фукнции разделены тематически:
- ООП — обеспечение (или, вернее сказать - эмуляция) возможности использовать принципы ООП в JavaScript
- Объектная модель JS — использование и расширение встроенных объектов JS
- Определение браузера — чтобы использовать в тех редких случаях, когда это все-таки неизбежно необходимо :)
- Координаты / Позиционирование — вычисление координат и позиционирование объектов - ввиду того, что это часто довольно хитрая штука
- DOM — работа с объектной моделью документа
- AJAX — вспомогательные функции для AJAX — так как это средство часто применимо :)
- Логгинг — иногда он нужен чтобы везде :)
NB! (советы по оптимизации и исправлениям приветствуются)
NB! (при написании статьи примеры были взяты из рабочего кода, но в некоторых навскидку изменены названия функций и даже некоторая функциональность. также к коду функций было применено такое форматирование как переносы строк - на данный момент - без проверки последующей работоспособности. как только код будет полностью проверен на работоспособность - этот комментарий будет отсюда удален)
ООП
1. Первый блок — набор из трех функций (две из которых пустые :) ), позволяющих применять (эмулировать?) все три принципа ООП в JavaScript. Из нескольких предложенных на AJAXPath и на AJAXPatterns вариантов я выбрал именно этот ввиду его одновременной понятности и быстрой скорости выполнения и немного его видоизменил так? чтобы отдельно объявленные свойства воспринимались как статические константы.
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; };
Полные примеры использования относительно велики, поэтому я их вынесу в следующую статью и проследую далее. Пару простых примеров вы можете наблюдать в пунктах 2, 5 и 15.
2. Следующая функция — простая, но изящная — полезна в сочетании с предыдущим набором — она создает функцию-ссылку на метод:
function createMethodReference(object, methodName) { return function () { return object[methodName].apply(object, arguments); }; }
Теперь можно, например, сделать так:
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');
Объект этого класса можно будет ассоциировать с событием скроллинга элемента с указанным ID и совершать что-либо по этому случаю.
Объектная модель JS
3. Нижеприведенная функция клонирует любой объект вместе со всеми его свойствами:
function cloneObj(objToClone) { var clone = []; for (i in objToClone) { clone[i] = objToClone[i]; } return clone; }
Использование — простейшее до невозможности:
var clonedObj = cloneObj(objToClone);
4. Конвертер объектов, следующая функция, позволяет удобно использовать всяческие условные (и претендующие ими быть :) ) конструкции вида if (tablet.toLowerCase() in oc(['cialis','mevacor','zocor'])) { alert(’I will not!’) };. Код заимствован отсюда.
function oc(a) { var o = {}; for(var i=0;i<a.length;i++) { o[a[i]]=''; } return o; }
Для примера возьмем ситуацию, когда сначала требуется определить, входит ли объект в какое-либо множество одиночных объектов, а затем - не входит ли он в сочетании с другим объектом в другое множество пар объектов. Допустим, на вечеринку пускают одиночек только с определенными именами, либо пары из списка с позволенными сочетаниями имен:
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
5. Функция, позволяющая создавать хэш сначала кажется немного излишней: объекты в JavaScript — те же хеши, но вот иногда в качестве имени проперти/ключа требуется задать значение значение переменной и тогда приходит на помощь функия Hash. (да-да, конечно же есть встроенные возможности, но так возможно просто немного очевиднее :) — можете исключить эту функцию из полезных, если хотите :) )
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]; } }
Доступ к элементам производится засчет свойства items (кстати, следует, может, в более тяжелой версии добавить keys :) ?):
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!");
6. Три других функции просто упрощают и/или делают очевиднее некоторые операции: getTime на 11 символов сокращает доступ к получению текущего времени, getTimeDelta позволяет найти промежуток в милисекундах между отрезками времени (или указанным моментом и текущим временем, в формате с одним параметром), а последняя функция расширяет свойства объекта Number для того чтобы при его значении NaN можно было чуть быстрее получить 0.
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; }
Определение браузера
7. Небольшой объект, поименованные по названиям браузеров свойства которого — суть условия. Этим достигается более читабельное (но не настолько скурпулезное насколько могло бы быть) определение большинства типов браузеров. Этот объект был заимствован мной из проекта, в котором я учавствовал — и как-то прижился, но, думаю, истинные авторы всё-таки где-то в сети, да и код не так уж сложен и громоздок чтобы на него сильно претендовать :). Кроме того, он конечно не идеально надежен (а некоторые говорят что не надежен вообще), но пока на перечисленных браузерах он меня не подвел ни разу :). Если вас не устраивает такое положение дел - вы можете использовать нечто похожее с HowToCreate. И повторюсь: данное определение я стараюсь использовать (как и сказано, например, по ссылке) "только в случае если известен конкретный баг в конкретном браузере и его нужно обойти". Также — несложно пересобрать этот объект в одно длинное условие, для меньшей скорости исполнения (см., опять же, ссылку)
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 } }
Координаты / Позиционирование
8. Набор функций, позволяющих получить координаты элемента на экране пользователя1. Если ваш документ статичен относительно окна и не имеет скроллбаров — лучше использовать функцию getPosition — так будет быстрее. В обратном случае используйте getAlignedPosition — она учитывает положения скроллбаров. Только обратите внимание: значение top или left у элемента может быть орицательным, если элемент частично расположен за пределами окна — для синхронизации с курсором мыши иногда нужно обнулить в этом случае высоту. Основной скрипт позаимствован из одного блога, Aligned-версия — результат поисков по сусекам и совмещения с информацией из двух статей (при обнаружении DOCTYPE IE входит в свой собственный, несколько непредсказуемый, режим). Также этот метод скомбинирован с получением позиций из исходников руководства по Drag’n'Drop. Обратите внимание: здесь используется функция NaN0 из пункта 6, вам нужно будет добавить ее в скрипт чтобы все работало как надо :) (спасибо, Homer).
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}; }
var IS_IE = USER_DATA['Browser'].MSIE; 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}; }
9. Определить текущие координаты курсора мыши и смещение элемента относительно курсора легко, если использовать соответствующие функции (собранные на основе трёх источников):
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 }; }
Последняя функция также может использоваться в двух режимах засчет атрибута aligned2 и предназначена для удобного использования в обработчиках событий, например:
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>
NB! (если данные функции (вдруг :) ) не заработают в каком-либо определенном случае — прошу сообщать — хочется добиться максимальной их переносимости)
10. Определение высоты элемента иногда более нелегкая задача чем определение других его параметров, но эти две функции придут на помощь:
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); }
DOM
11. Иногда нужно пройти рекурсивно по дереву DOM, начиная с некоторого элемента и выполняя некоторую функцию над каждым из потомков, забираясь в самую глубь. В DOM есть объект TreeWalker, но он не работает в IE и не всегда удобен/прост в использовании. Функция walkTree позволяет выполнить некоторую другую функцию над каждым из элементов и позволяет также передать в нее некоторый пакет данных. Функция searchTree отличается от нее тем, что останавливает проход по дереву при первом удачном результате и возвращает результат в точку вызова:
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; } }
В примере используются функции setElmAttr и getElmAttr, которые будут рассмотрены позже - в пункте 13. По сути они делают то же что и getAttribute и setAttribute. Пояснения к используемой функции oc вы можете посмотреть в пукте 4. В первой части примера корневому элементу атрибут "nodeType" устанавливается в "root", а всем его потомкам - в "child". Во второй части демонстрируется также передача пакета данных — при нахождении первого элемента с атрибутом "class", равным одному из перечисленных в пакете имен, атрибут "isTarget" ему устанавливается в "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);
NB! (будьте осторожны с использованием этих функций и постарайтесь избежать их чересчур частого вызова (более раза в секунду) даже на средней ветвистости дереве - они могут пожрать немало ресурсов. или, по крайней мере, вызывайте их в фоне через setTimeout)
12. Удаление узлов - иногда необходимая задача. Иногда нужно удалить сам узел, а иногда — только его потомков. Функция removeChildrenRecursively рекурсивно удаляет всех потомков указанного узла, не затрагивая, конечно, его самого. Функция removeElementById, как и сказано в названии, удалает узел по его id - при всей простоте задачи способ относительно хитрый:
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)); }
13. Казалось бы — элементарная задача работы с атрибутами элемента — иногда наталкивает на абсолютно неожиданные проблемы: например, IE бросает исключение при попытке доступа к атрибутам высоты/ширины элемента table, а у Safari отличается способ доступа к атрибутам с пространствами имен. Приведенные ниже функции обходят все встреченные мной проблемы без сильного ущерба к скорости выполнения (конечно же, в стандартных случаях лучше использовать встроенные функции):
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); } }
Засчет универсальности появляется некоторая неудобочитаемость ввиду того, что необязательный атрибут пространства имен — последний. Решения приветствуются.
AJAX
14. Если вам не нужно ничего большего, чем просто выполнить асинхронный запрос и на основе полученных данных сделать нечто — для вас эта функция. Способ получения объекта XMLHttpRequest безусловно может быть заменен. Комментарии намеренно оставлены, дабы показать некоторые идеи по расширению:
/* AJAX call */ /* locationURL - URL to use */ /* parameters - url parameters, null if not required (format: "parameter1=value1¶meter2=value2[...]") */ /* onComplete - listener: function (http_request) or (http_request, package) */ /* doPost - (optional) specifies if POST (true) or GET (false/null) request required /* package - (optional) some variable or array to tranfer to complete listener, may be not specified */ function makeRequest(locationURL, parameters, onComplete, doPost, dataPackage) { var http_request = false; try { http_request = new ActiveXObject("Msxml2.XMLHTTP"); } catch (e1) { try { http_request= new ActiveXObject("Microsoft.XMLHTTP"); } catch (e2) { http_request = new XMLHttpRequest(); } } //if (http_request.overrideMimeType) { // optional // http_request.overrideMimeType('text/xml'); //} if (!http_request) { alert('Cannot create XMLHTTP instance'); return false; } completeListener = function() { if (http_request.readyState == 4) { if (http_request.status == 200) { onComplete(http_request, dataPackage) } } }; //var salt = hex_md5(new Date().toString()); http_request.onreadystatechange = completeListener; if (doPost) { http_request.open('POST', locationURL, true); http_request.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); http_request.setRequestHeader("Content-length", parameters.length); http_request.setRequestHeader("Connection", "close"); http_request.send(parameters); } else { http_request.open('GET', locationURL + (parameters ? ("?" + parameters) : ""), true); //http_request.open('GET', './proxy.php?' + parameters + // "&salt=" + salt, true); http_request.send(null); } }
Пример использования — из одного моего рабочего тестового задания, которое занималось поиском в базе музыки и/или фильмов по введенной в элемент (с id "searchStr") строке, используя SQL’ный LIKE:
function gotSearchResults(http_request, dataPackage) { request_result = http_request.responseText; var divElement = document.getElementById(dataPackage["divId"]); divElement.innerHTML = request_result; } function insertMusicSearchResults(divId) { var searchStrElement = document.getElementById("searchStr"); var dataPackage = new Array(); dataPackage["divId"] = divId; makeRequest("getAlbums.php", "searchStr=" + searchStrElement.value, gotSearchResults, false, dataPackage); } function insertVideoSearchResults(divId) { var searchStrElement = document.getElementById("searchStr"); var dataPackage = new Array(); dataPackage["divId"] = divId; makeRequest("getMovies.php", "searchStr=" + searchStrElement.value, gotSearchResults, false, dataPackage); }
Логгинг
15. Представленная ниже функция для помощи в ведении логов очень проста, добавьте в нужное место в документе элемент <div id="LOG_DIV"></div>, задайте ему необходимую высоту, и в него будет сбрасываться информация + обеспечиваться ее скроллинг:
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; } }
16. В замечательном плагине Firebug для браузера Firefox есть замечательная консоль, в которую с широкими возможностями можно производить логгинг. Однако, если вы отлаживаете параллельно код в других браузерах — обращения к ней могут вызывать ошибки и даже крэши. Для того чтобы не очищать каждый раз код от логов, можно использовать такую заглушку:
var Console = Class.extend({ // the stub class to allow using console when browser have it, // if not - just pass all calls construct: function() {}, log: function() { }, info: function() { }, warn: function() { }, error: function() { } }); if (!window.console) { console = new Console(); }
Сочетание этого и предыдущего пункта + CSS может вдохновить вас на написание собственной консоли с функциональностью консоли Firebug, но для других браузеров ;). Если вы ее напишете - поделитесь, пожалуйста, со мной :).
Бонус
В качестве бонуса (чтобы не портить приятно отдающее двоичностью число в заголовке :) ) рассажу о проблеме двойного клика — бился над ней не я, а мои коллеги, решение также сетевое — но в некоторой обработке. Проблема состоит в том, что при регистрации события ondblclick все равно вызывается событие onclick. Поэтому, если уж очень это событие (неочевидное, стоит заметить, для пользователя сети) необходимо - лучше всего иметь в скриптах что-то вроде такого кода (с необходимым вам количеством миллисекунд и сохраняя, если необходимо, элемент, на котором был совершен клик):
var dblClicked = false; var dblClickedNode = null; var DBL_CLICK_MAXTIME = 300; function dblClick(clickedNode) { dblClicked = true; dblClickedNode = clickedNode || dblClickedNode; } function releaseDblClick() { setTimeout('dblClicked=false;', DBL_CLICK_MAXTIME); }
Его использование накладывает относительно сложные условия. Теперь в обработчике ondblclick нужно вызывать сначала первую функцию, затем - закончив собственно обработку - вторую, а в обработчике onclick проверять, не совершен ли двойной клик:
<div id="someId" onclick="if (!dblClicked) alert('click');" ondblick="dblClick(this); alert('dblclick'); releaseDblClick();";></div>
Также, к пункту 1 можно добавить небольшую функцию получения инстанса (на ваше усмотрение вы можете изменить ее так, чтобы она предавала аргументы в конструктор):
function getInstanceOf(className) { return eval('new ' + className + '()'); }
К пункту 6 подойдет функция паузы (именно паузы, а не выполнения в отдельном поптоке, как делает setTimeout):
function pause(millis) { var time = new Date(); var curTime = null; do { curTime = new Date(); } while (curTime - time < millis); }
Upd. Ещё пара функций, относящихся к пункту 6:
Определение Вхождения числа в область чисел, ограниченную числом start спереди включительно и числом stop в конце исключительно:
Number.prototype.inBounds=function(start,stop){return ((this>=start)&&(this<stop))?true:false;};
Срезание начальных и конечных пробельных символов строки:
String.prototype.trim=function(){var temp = this.replace( /^\s+/g, "" );return temp.replace( /\s+$/g, "" );}
Преобразование объекта и строки в тип boolean. Для boolean-объектов метод также описан, ввиду того, что данные о типе переданного объекта (строка или boolean) могут быть неизвестны:
function boolFromObj(obj){return(((obj=="true")||(obj == true))?true:false);} String.prototype.asBoolVal=function(){return ((this=="true")?true:false);} Boolean.prototype.asBoolVal=function(){return ((this==true)?true:false);}
Дополнение нулями числа до тех пор, пока количество цифр в нём не достигнет указанного:
Number.prototype.getFStr=function(fillNum){var fillNum=fillNum?fillNum:2;var temp=""+this;while(temp.length<fillNum)temp="0"+temp;return temp;}
Кроме этого, ко второй части можно отнести функции, связанные с сортировкой,…
function intComparator(a, b) { return a - b; }
] function getObjSortedProps(obj, sortFunc) { var propsArr = []; for (propName in obj) { propsArr.push(propName); } return propsArr.sort(sortFunc); }
…где функция getObjSortedProps позволяет получить массив из отсортированных (с применением указанного компаратора sortFunc) имён свойств переданного объекта, а функция intComparator может быть передана функции массивов sort или той же самой getObjSortedProps, если нужный массив или имена свойств объекта содержит/содержат числовые значения…
…и две функции для работы с массивами:
function indexOf(arr, elem) { for (itemIdx in arr) { if (arr[itemIdx] == elem) return itemIdx; } return null; }
function removeFromArray(arr, element) { // removes only one item! for (itemIndex in arr) { if (arr[itemIndex] == element) { arr.splice(itemIndex, 1); return arr; } } return null; }
indexOf возвращает индекс указанного элемента в переданном массиве, а функция removeFromArray удаляет из указанного массива переданный элемент.
Заключение
Ну вот — кажется, пока всё. Статья — в состоянии готовности к исправлениям (если понадобятся :) ), можно переходить к следующим :). В следующей статье я намереваюсь рассказать поподробнее про ООП в JavaScript и привести в пример пару простых, но полезных классов. Надеюсь, эта статья вам помогла и хоть немного сократила имеющие потенциальную возможность быть потраченными на решение всяких причуд браузеров рабочие человекочасы.





