Привет всем.
Как вы уже поняли из названия статьи, я не люблю JS framework'и.
Меня пугает их размер, медлительность и неизвестность…
Да-да, именно 'неизвестность', даже когда есть берешь 'чистые' исходники без какой-либо компрессии и тд., сложно проследить весь путь исполняемой функции. Сколько раз на работе сталкивался с тем что элементарная задача, типа: «Удалить целую строку из html таблицы, при клике на кнопку» — заканчивалась error'ом в IE. Именно поэтому я и задумался над созданием своего 'джентльменского' набора для работы с JS.
Что-то было найдено на просторах javascript.ru, что-то — на просторах Интернета, а что-то и было написано мной.
Большинство функций каждый из вас уже видел, поэтому я буду задерживаться только на интересных моментах (на мой взгляд).
Итак, приступим:
// простой debug, лень писать каждый раз console.log(),
// на момент написания ее, про использование более одного параметра, мыслей не было, так и живем
/**
* Debug function
*
* @version 2012-07-16
* @param mixed param
* @return void
*/
function _d(param)
{
console.log(param);
}
// ну тут все понятно
/**
* Function return random integer value
*
* @version 2012-07-22
* @param integer minV
* @param integer maxV
* @return integer
*/
function getRandomInt(minV, maxV)
{
if(minV == undefined)
minV = 0;
if(maxV == undefined)
maxV = 10;
return Math.floor(Math.random() * (maxV - minV + 1)) + minV;
}
// здесь я тоже думаю, проблем не возникнет
/**
* Function return element by id
*
* @version 2012-07-22
* @param string id title of id
* @return Object
*/
function $(id)
{
return document.getElementById(id);
}
// Здесь хочется сказать про то, что если используется 'нормальный' браузет - все ok,
// а вот если нет, то предется обойти всех по-порядку и спросить его className
/**
* Function return element by class name
*
* @version 2012-11-07
* @param string classList
* @param object node
* @return Object
*/
function $$(classList, node)
{
if(document.getElementsByClassName)
return (node || document).getElementsByClassName(classList);
else{
var node = node || document,
list = node.getElementsByTagName('*'),
length = list.length,
classArray = classList.split(/s+/),
classes = classArray.length,
result = [], i,j;
for(i = 0; i < length; i++) {
for(j = 0; j < classes; j++) {
if(list[i].className.search('\b' + classArray[j] + '\b') != -1) {
result.push(list[i])
break
}
}
}
return result;
}
}
Здесь заканчивается первая партия функций, и начинается вторая (первые две, я думаю все уже встречали, поэтому они не нуждаются в пояснениях):
/**
* Function attach event to element
*
* @version 2012-07-22
* @param Object elem dom element
* @param string type event type
* @param mixed handler
* @return Object
*/
function addEvent(elem, type, handler)
{
if(!elem)
_d('Error: invalid param in `addEvent()`.');
if (elem.addEventListener)
elem.addEventListener(type, handler, false);
else if (elem.attachEvent)
elem.attachEvent("on" + type, handler);
}
/**
* Function deattach event to element
*
* @version 2012-07-22
* @param Object elem dom element
* @param string type event type
* @param mixed handler
* @return Object
*/
function removeEvent(elem, type, handler)
{
if(!elem)
_d('Error: invalid param in `removeEvent()`.');
if (elem.removeEventListener)
elem.removeEventListener(type, handler, false)
else if (elem.detachEvent)
elem.detachEvent("on" + type, handler)
}
// Данная функция вернет Event объект, с некоторыми дополнениями.
// Главная ее фишка в том что она вернет наиболее популярные свойства и как в IE, и так в остальных браузерах
/**
* Function return Cross-browser object Event
*
* @version 2012-08-17
* @param Object e event Object
* @return Object
*/
function getEvent(e)
{
// get event element
event = e || window.event
// function edit event element one time
if ( event.isFixed )
return event
event.isFixed = true
// add 'preventDefault/stopPropagation' for IE
event.preventDefault = event.preventDefault || function(){this.returnValue = false}
event.stopPropagation = event.stopPropagaton || function(){this.cancelBubble = true}
// add 'target' for IE
if (!event.target)
event.target = event.srcElement
// add 'relatedTarget' for IE, if need
if (!event.relatedTarget && event.fromElement)
event.relatedTarget = event.fromElement == event.target ? event.toElement : event.fromElement;
// add 'pageX/pageY' for IE
if ( event.pageX == null && event.clientX != null ) {
var html = document.documentElement, body = document.body;
event.pageX = event.clientX + (html && html.scrollLeft || body && body.scrollLeft || 0) - (html.clientLeft || 0);
event.pageY = event.clientY + (html && html.scrollTop || body && body.scrollTop || 0) - (html.clientTop || 0);
}
// add 'which' for mouse in IE
// 1 == left; 2 == middle; 3 == right
if ( !event.which && event.button )
event.which = (event.button & 1 ? 1 : ( event.button & 2 ? 3 : ( event.button & 4 ? 2 : 0 ) ));
return event;
}
// Я думаю, что каждый js web-разработчик сталкивался с тем что,
// необходимо выполнить какой-либо JS код, после загрузки страницы (но не всех фото и тд.)
// Данная функция для этого и предназначена.
// Если никак браузер, ни как не смог этого сделать, функция выполнится по стандартному onLoad()
/**
* Function attach one event on dom ready
*
* @version 2012-10-27
* @param mixed handler
* @return void
*/
function bindReady(handler)
{
var called = false;
// simple function
function ready() {
if (called)
return;
called = true;
handler();
}
// if brouser support event "DOMContentLoaded"
if ( document.addEventListener ) {
document.addEventListener( "DOMContentLoaded", function(){
ready()
}, false );
}
else if ( document.attachEvent ) {
// if IE brousers
if ( document.documentElement.doScroll && window == window.top ) {
function tryScroll(){
if (called)
return;
if (!document.body)
return;
// try scroll page, else try scroll through some time
try {
document.documentElement.doScroll("left");
ready();
} catch(e) {
setTimeout(tryScroll, 0);
}
}
tryScroll()
}
// if brouser support event "onreadystatechange"
document.attachEvent("onreadystatechange", function(){
if ( document.readyState === "complete" )
ready()
});
}
// attach to standart onLoad event
if (window.addEventListener)
window.addEventListener('load', ready, false)
else if (window.attachEvent)
window.attachEvent('onload', ready)
window.onload=ready
}
// Данная переменная, и функция описанная ниже, нужны для того,
// чтобы можно было вешать несколько обработчиков, одновременно
// initialize array for onReady()
readyList = [];
/**
* Function attach some dom ready events
*
* @version 2012-10-27
* @param mixed handler
* @see bindReady()
* @return void
*/
function onReady(handler)
{
if (!readyList.length) {
bindReady(function() {
for(var i=0; i<readyList.length; i++)
readyList[i]();
});
}
readyList.push(handler)
}
// Если кто-то не понял, то пример использования:
onReady(function () {
alert('Первый...');
});
onReady(function () {
alert('А может быть и не первый...');
});
Ну и наконец, последняя порция JS на сегодня:
// Эта функция вернет объект вида:
// {top: xxx, left: xxx, right: xxx, bottom: xxx, width: xxx, height: xxx}, где xxx - количество пикселей
// Одно но, как-то на днях, нужно было получить положение вспарывающего дива, и все было нормально, пока не трогали scroll
// Так и был рожден фикс насчет, obj.style.position ='absolute'
/**
* Function return all coordinates for element
*
* @version 2012-10-26
* @param Object obj dom element
* @param boolean absolute if position = `absolute` or `fixed`
* @return Object
*/
function getCoordinates(obj, absolute)
{
if(!obj)
obj = window;
// get block box
if(obj != window && obj != document){
var box = obj.getBoundingClientRect();
var width = obj.offsetWidth;
var height = obj.offsetHeight;
}
else{
var box = new Object({'top':0, 'left':0});
var width = window.innerWidth || document.documentElement.clientWidth;
var height = window.innerHeight || document.documentElement.clientHeight;
}
// get body
var body = document.body;
var docEl = document.documentElement;
// get scroll params
if((absolute != undefined) && absolute){
var scrollTop = 0;
var scrollLeft = 0;
}
else{
var scrollTop = window.pageYOffset || docEl.scrollTop || body.scrollTop;
var scrollLeft = window.pageXOffset || docEl.scrollLeft || body.scrollLeft;
}
// get shift in IE
var clientTop = docEl.clientTop || body.clientTop || 0;
var clientLeft = docEl.clientLeft || body.clientLeft || 0;
var top = Math.round(box.top + scrollTop - clientTop);
var left = Math.round(box.left + scrollLeft - clientLeft);
return { 'top': top, 'left': left, 'right': left+width, 'bottom': top+height, 'width':width, 'height': height };
}
// Стандартная проверка на содержимое класса
/**
* Function return TRUE if element has class
*
* @version 2012-07-22
* @param Object elem dom element
* @param string classname class name
* @return boolean
*/
function hasClass(elem, classname)
{
return (' '+elem.className+' ').indexOf(' '+classname+' ') > -1;
}
// Добавляем новый класс, если такого нету
/**
* Function add new class for element
*
* @version 2012-07-27
* @param Object elem dom element
* @param string classname class name
* @return boolean
*/
function addClass(elem, classname)
{
if(!hasClass(elem, classname))
if(elem.className != '')
elem.className += ' '+classname;
else
elem.className = classname;
}
// ... удаляем - если есть
/**
* Function remove class from element
*
* @version 2012-07-27
* @param Object elem dom element
* @param string classname class name
* @return boolean
*/
function removeClass(elem, classname)
{
if(hasClass(elem, classname))
elem.className = trim(elem.className.replace(new RegExp('(^|\s)' + classname + '(?:\s|$)'), '$1'));
}
// Последняя функция на сегодня - аналог php trim()
// Если не изменяет память, позаимствовал у Mootools :)
/**
* Function remove spaces in begin and end of string
*
* @version 2012-11-05
* @param string str
* @return string
*/
function trim(str)
{
return String(str).replace(/^s+|s+$/g, '');
}
Вот пожалуй и все.
Если кому-то данные записки сумасшедшего пригодятся — напишу продолжение…
PS. Исходники будут доступны после второй части статьи.
Автор: mamchyts