Pull to refresh

Comments 66

P.S. Попытаюсь опередить вашу критику того, что в яваскрипт включены стили — мол это не красиво, и стили должны находится в отдельном CSS-файле. Да, действительно — в этом файле смешан и дизайн, и программа, что есть нарушение общепринятого соглашения. Но дело в том, что красота того, что для решения задачи от начала и до конца нужен всего один файл пересилила все безобразие смеси CSS и JavaScript. Поэтому я все-таки остановился на этом варианте
Давайте разговаривать в конструктивном ключе. Я открыт к диалогу и любой критике, если она делает явными допущенные ошибки. Попытаюсь обосновать свой выбор. Для меня главным показателем красоты является соотношение полученного эффекта к затраченному времени. Как в краткосрочной так и в долгосрочной перспективах.

Вот как я распределяю красивые решения, по мере увеличения увеличения их красоты:

1) быстро сделать сейчас — долбаться потом (костыли)
2) долго долбаться сейчас, быстро делать потом (CMS, фреймворки и т.п.)
3) быстро сделать сейчас и не долбаться потом. Самые удачные решения

Примером красивого решения является, на мой взгляд, JQuery — легко подключить, не требует настройки, не заставляет долбаться и в дальнейшем. Такие решения всегда оцениваются и становятся популярными, потому как в момент выбора инструмента перед разработчиком всегда стоит два вопроса:

1) как быстро я решу поставленную задачу с помощью данного инструмента
2) на что я буду обречен, воспользовавшись этим иснтрументом

Если удовлетворить эти две тревоги — инструмент обречен на удачу.

Создать инструмент такой категории — это сродни написания стиха — чтобы и рифма в нем была, и смысл сохранился. Это не так просто, и тут обычная логика бессильна — это постоянный поиск и муки творчества. Либо рифма ломается (скорость решения задачи падает) или смысл теряется (система становится не расширяемой). А что Вы имеете ввиду под красотой?
Главный показатель красоты — это читабельность алгоритма действия и беспрепятственная работа с вашим кодом не только JS-программиста, раз уж вы работаете с интерфейсом, но и верстальщика, и дизайнера, и бог весть кого, что захочет отредактировать финальный html и придет в ужас от того, что для изменения цвета бэкграунда нужно что-то править в JS. Нет, не в CSS, как у всех нормальных людей, а в JS. Вы ведь не свой личный бложег пишете, а, как я понял, какое либо решение для других людей.

Посмотрите на jQuery.UI. Вполне себе адекватная библиотека. С внешними стилями. Полностью изучаема и кастомизабельна. И, заметьте, не только спецами-программерами, собаку съевшими в JS-джунглях.

Надеюсь, мое понятие красоты кода вы осознали.
в чем проблема переопределить стили в классах hotsitemap и hotprompt?
Многие плагины идут такой дорогой, т.к. скрипт должен быть подключён и сразу после этого заработать. Подход с недостатками, да, но если не указать стили по-умолчанию, то многие его прикрепят, скажут, что ничего не работает и пойдут искать другой.
хотя, с другой сторононы, многие плагины выносят ксс в отдельный файл и нормально живут при этом.
Вы сами ответили на свой вопрос.
Да, многие идут этой кривой дорожкой, согласен. Но лучше положить в бандл css-файлик + инструкцию о том, как этот файлик должен быть заинклюжен, чем портить себе карму проклятиями от других разрабов, что на изменение цвета бордюра в ваших исходниках у него ушло больше времени, чем на написание такого же плагина своими силами.
Хорошо, согласен, убедили. Eсли для вас важно чтобы дизанер мог легко менять стиль — выносите его в hotkey.css
Зачем в отдельный hotkey.css? Оптимизаторы хайлоадов наоборот думают, как меньше запросов к серверу делать (спрайты, пакинг, all-in-one etc.).

Спокойно в дефолтном css хранить стили.
Отдельный файл удобно для группировки стилей вокруг задачи — клавиши в hotkeys.css, вкладки во tabs.css, разметка в файле layout.css. А для оптимизации запросов на сервер лучше применять скрипт компилирующий стили в один файл. Тогда и разработчики не скроллят трехметровый css и сервер доволен :)
Еще один момент, если стили разделены на много файлов, то это позволяет в случае наличия системы скинов — перегружать отдельные файлы css — не переписывая в отдельно взятом скине весь CSS модуля. Легче реализуется наследование отображений
чтобы приделать к своему сайту нужно будет менять оформление и как следствие твой скрипт. потом ты сделаешь ему рефакторинг и все изменение придётся примерживать вручную к новой версии.

лучше хранить в отдельных файлах, но дать специальный сборщик, который сделает из кучи файлов 1 бандл.

хранить хоткеи в аттрибутах дурная практика, ибо порождает конфликты. например на сайте 10 разделов. в одном используется ктрл+К, потом нужно добавить ещё один раздел и ему подходит комбинация ктрл+К. пойдёшь проверять все раздели не используется ли где? скорее всего нет. хоткеи должны быть глобальны, чтобы пользователю не приходилось помнить в каком разделе он сейчас находится и подбирать в уме правильную комбинацию в зависимости от текущего положения и того где он хочет оказаться. единственное что имеет значение — куда он хочет попасть

ничто не мешает использовать свои аттрибуты и при этом иметь валидный документ. не ной. пишешь свой дтд и вперёд.

на работе надо работать а не статьи на хабр писать. променял зарплату на хабра-фантики
вы его начальник?:) Если нет, то заплату свою он и так получит
Хоткеи не обязаны быть глобальными, обычно они зависят от страницы, на которой вы находитесь. Так же как и в винде — в одной программе одна комбинация делает одно, в другой программе эта же комбинация делает другое. Они не конфликтуют благодаря тому что существуют в каком-то контексте.
они конфликтуют в голове пользователя. один и тот же хоткей по хорошему должен в каждой программе выполнять одно и то же. утопично, но к этому надо стремиться.
Вопрос спорный, поэтому я полагаю это должно решаться на уровне разработчика сайта, а не на уровне движка горячих клавиш. Секрет успеха любой библиотеки в том, что она не навязывает своих решений, а лишь предлагает инструмент
На счет хабра фантиков. Пока что я никаких фантиков не получал, не считая нескольких полезных знакомств. Я просто делюсь своими решениями и мне приятно, если кто-то этим пользуется

Например, если я обычный веб-мастер/администратор/пользователь выданного мне «под ключ» сайта, владеющий только базовыми знаниями css и html. То я расшибусь пытаясь понять как сделать всплывающее меню другого цвета.

Тут даже стили нельзя помять внешним файлом, так как методы css прописывают их жестко в тело атрибута и любые классы внешние css никак их уже не изменят, даже если определять их к классу hotprompt. Так что это не стили «по умолчанию», а это стили без выбора.

И просто мелочь… логичнее было бы писать так для множества стилей:

*.css({
«border»: «1px solid»,
«color»: «red»,

})
Через !important можно поменять
«Обычный веб-мастер/администратор/пользователь» не догадывается об этом и это совсем не изящно, так же как танком колоть орехи.
верстальщик, главное, догадывается.
вот он обрадуется верстать без селекторов
а зачем тут селекторы? есть два класса, которые можно менять.
Колоть танком орехи — прикольно. Можно легко попасть в книгу рекордов, как человек, расколовший рекордное количество орехов танком :)
Тут надо понимтать, что CSS по большей части является конфигурационной составляющей «библиотечки». Было бы очень неудобно если бы вы к примеру выложили код скрипта в SVN, народ бы начал ею активно пользоваться и настраивать стили под свои проекты, а потом бы в к примеру обнаружили баг, сделали патч и закомитили. Пользователи не смогли бы так просто взять и обновиться, а были бы вынуждены колупаться в исходнике заново меняя настройки стилей.
По поводу нажатых клавиш:

1. Регистрируйте только первое нажатие, и, соответственно, обрабатывайте только первое отжатие какой-либо клавиши. Очень неудачно выглядит на вашем сайтике, допустим, зажатие клавиши Esc больше чем на 1 секунду.

2. hotkey не нужен. Для этого есть javascript.

3. Ctrl я, например, нажимаю не только для навигации по сайту. Отображать help при нажатии на Ctrl — первый шаг к раздражению лично меня. Особенно, когда ваша подсказка остается висеть при потере фокуса.

4. За совпадение хоткеев браузера и ваших хоткеев никто вам спасибо не скажет.

Если есть желание дальше ковыряться с клавой в JS — могу помочь.
Если используете jQuery 1.4 и далее почитайте про новые фичи,

в частности, можно писать

$('html').bind({
	keyDown: function(e){
	},
	keyUp: function(e){
	},
	click: function(e){
	}
})

Появилась ф-я detach(), которая удаляет объекты из DOM, но сохраняет их свойства, соответственно не надо заново настраивать свойства объектов каждый раз. Это быстрее.

Проверку на тип можно сделать так

lastGood.is('input[type=submit]') ? lastGood.submit() : lastGood.click()


Ещё много где можно отшлифовать код.

Ну и соглашусь, что не труъ мешать оформление и функционал. У вас нет css файла в проекте?
Кстати! Благое дело! Подправьте, пожалуйста, код в соответствии со своими рекомендациями и выкладывайте сюда. Я думаю все будут только за
Пожалуйста, мой вариант обработки HotKeys.
Вы забыли в своём плагине про keypress, в котором надо останавливать выполнение HotKeys браузера.

Присваиваете элементу класс «hotkey» и rel=«hot+key», демо, jsbin


var hotkeys = {
  48: '0', 49: '1', 50: '2',
  16: 'shift',
  17: 'ctrl',
  18: 'alt',
  13: 'enter',
  32: 'space',
  38: 'up',
  40: 'down',
  27: 'esc',
  191: '/'
}
var keys = [];

$(window).bind({

  keypress: function(e){
    // Prevent browser bulit-in HotKeys
    if(hotkeys[e.which ? e.which : e.keyCode]) e.preventDefault();
  },

  keydown: function(e){
    if(e.keyCode == 17){ // Ctrl
      // Show HotKeys Tip
    }
    if(hotkeys[e.keyCode]) keys.push(hotkeys[e.keyCode]);
  },
 
  keyup: function(e){
    var o = $('.hotkey[rel='+keys.join('+')+']');
    o.is('input[type=submit]') ? o.submit() : o.click();
    keys = []; // clear array
    
    if(e.keyCode == 17){ // Ctrl
        // Hide HotKeys Tip
    }
  }
  
});


Дальше — сами ;-)
Еще можно наткнуться на интересные браузеры, которые Ctrl+Shift+U обрабатывают как Ctrl+U. И об этом тоже нужно думать.
Знаешь, я уже после того как поигрался с эвентами, погуглил плагины — Ресиг написал плагин для хоткеев на базе существовавшего плагина для версий до 1.4. А мы тут велосипедим)
это было после того, как свой велосипед писал я.
CSS стили, генерируемые скриптом, можно делать не инлайнывыми, а создавать тег <style>. В этом случае такие стили легко будет переопределяться.
>> Для работы требуется JQuery.
это не 138 строк, это расширение для jQuery :/
Позвольте опять попридираться у деталям.

// прошу простить, тут не все коды клавиш

Почему? Вот вам почти все коды: pastebin.com/E17uN3ex

Несколько раз повторяется указание border-radius в статье. Это можно было бы решить, написав свой мини плагин для JQuery. Или, хотя-бы, вынеся в отдельную приватную функцию.
JQuery.fn.borderRadius = JQuery.fn.borderRadius || function (value) {
	return $(this).css({
		"-webkit-border-radius": value,
		"-moz-border-radius"   : value,
		"border-radius"        : value
	})
};


То, как вы указываете CSS — просто ужасно. Хотя такой подход сам по себе плох (хотя, конечно, кое-в-чём оправдан и многие так делают), но намного красивее было бы, если бы вы это сделали через объект + воспользовались нашим плагином:
var prompt = $("<div class='hotprompt'>-</div>")
	.css({
		background : "orange",
		border     : "1px solid black",
		color      : "black",
		fontSize   : "8px",
		opacity    : "0.8", // легкая полупрозрачность не повредит
		padding    : "1px 3px",
		position   : "absolute",
	})
	.borderRadius("7px 7px 0px 7px");


Во многих функциях вы много раз используете $(this). Но, желательно, такое кешировать, т.к. обвертка dom-элемента в jquery-элемент занимает время.

// скрыть эти подсказки
var hideHotPrompts = function(){
	// путем их тупого удаления:
	$("a[hotkey], input[hotkey]").each(function(a){
		$(".hotprompt").remove();
	});
}

Вы уверены, что это то, что вы хотели написать? т.к. в том виде, в котором оно есть сейчас проще и правильнее было бы его сделать так и:
var hideHotPrompts = function(){
	if ($("a[hotkey], input[hotkey]").length) $(".hotprompt").remove();
}


Не нужен: var in_array = function(needlehaystack)
Т.к. есть: jQuery.inArrayvalue, array )

Категорически не рекомендую перебирать так элементы объекта Array. Тоже сначала так делал (и, между прочим, тоже когда писал KeyboardListener на JQuery) и потом пришлось всё переделывать. Только так:
// Плохо
for(var i in words)

// Хорошо
for(var i = 0, l = words.length; i<l; i++)

// Хорошо, но в обратном порядке
for(var i = words.length; i--;)


// если куда-нибудь кликнули - скрываем все подсказки и карту клавиш

Не согласен. Я нажал Ctrl и хочу найти необходимую мне комбинацию. Щёлкаю на документе и начинаю прокручивать его колёсиком.

А в чём конфликт работы и статтей на Хабре?
Зачем вы учите плохому?
в jQuery для таких вещей (проход по всем элементам массива/объекта) давно используется вот это:

    jQuery.each(words, function(idx, val){
        // do something
    })


Если у вас будет массив с первым и тысячным элементом — ваши «Хорошие» конструкции без if'a внутри будут обрабатывать пустые элементы 998 раз.
естественно. если вы не знаете, какое будет сожержимое массива (но в случае split такого не произойдёт), то правильный код такой:
for(var i = 0, l = words.length; i<l; i++) if (i in words) {
    // тело цикла
}


Но jQuery.each — это не более правильный вариант. Это другой вариант. В нём, например, нету такого ключевого слова, как break (по крайней мере не заметил в мануале), и нельзя сделать return из функции:

function match () {
	for(var i = 0, l = elems.length; i<l; i++) if (i in elems) {
		if (fail(elems[i])) break;

		if (success(elems[i])) {
			run(elems[i]);
			return true;
		}
	}
	return false;
}
break'нуть jQuery.each() можно вернув false из коллбэка.

Мне кажется, что если приходится использовать конструкции вроде вашего последнего match() — что-то уже более глобально не так, чем неправильное использование циклов.

Но это уже мое имхо, не обращайте внимания на старого брюзгу
ну почему? in_array только так и пишется. это нормальная конструкция. «все нашли/сделали, следующие элементы массива можно не обходить». Я такое видел в т.ч. во внутренностях многих крутых фреймворков.
и получите кучу проблем. все-равно придётся переделывать этот кусок.
об этом я и писал. посмотрите первый коммент этой ветки
Ваш вариант всё равно будет делать 1к итераций в данном случае.
И по-моему, в моём варианте хватит добавить проверку на то, что i — число, что бы решить все проблемы, не?
Я думаю, сакральный смысл такого подхода (почему во многих фреймворках так делается) вот в чём (может еще какие подводные камни есть):

shock@localhost:~> cat array-test.js
var arr = ['zero','one','two'];                                                                        
arr[ 7] = 'seven';                                                                                     
arr[ 5] = 'five';                                                                                      
arr[-1] = 'minus one';                                                                                 
arr[1.123] = 'float';                                                                                  

console.log('\n*** typeof i');

for (var i in arr) {
        console.log(i, typeof i);
        if (i == 2) break;
}

console.log('\n*** typeof i == number');

for (var i in arr) if (typeof i == 'number') {
        console.log(arr[i]);
}

console.log('\n*** !isNaN(i)');

for (var i in arr) if (!isNaN(i)) {
        console.log(arr[i]);
}

console.log('\n*** i = 0, l = arr.length; i < l');

for (var i = 0, l = arr.length; i < l; i++) if (i in arr) {
        console.log(arr[i]);
}

console.log('\n*** arr.forEach');

arr.forEach(function (item) {
        console.log(item);
});


shock@localhost:~> node array-test.js

*** typeof i
0 string
1 string
2 string

*** typeof i == number

*** !isNaN(i)
zero
one
two
five
seven
minus one
float

*** i = 0, l = arr.length; i < l
zero
one
two
five
seven

*** arr.forEach
zero
one
two
five
seven
Олег, а какая связь у прощании с работой и статьёй на хабре?
Видимо, — писал вместо работы?
Видимо, решил из того, что писал, сделать новую работу. Другого объяснения появления компании «Микросеть» на хабре я не вижу.
ну почти… ночью писал про плагины, утром опоздал, оштрафовали, уволился
о! ещё один плюс в сторону моей удалённой работы
Хоткеи — атрибут программы, не вижу смысла и причин навешивать их на контент. Необходимые хоткеи браузера быстро запоминаются и потом используются; если при этом на каждом сайте (как того хочет ТС) будут использоваться свои хоткеи на самые разнообразные действия — наступит кромешный ппц. Неужели программисту так сложно это понять?
Вас не напрягает хоткей хабра Ctrl+Enter?
Хочу сообщить вам, что сайты уже давно стали «веб-приложениями». И выглядят они уже не так примитивно как вы думаете.
Да да, именно так! Сайт можно писать как программу. Вообще не заморачиваясь с дизайном — и этой программой будут пользоваться, если она решает проблемы пользователя
Будут пользоваться ровно до тех пор, пока не сделают аналогичное, но и ещё удобное. Не надо недооценивать важность дизайна (информационного, юзабилити)
	$("html").keydown(function(e){
		var lastGood = false;
		// то перебираем все ссылки и инпуты, у которых есть атрибут hotkey
		$("a[hotkey], input[hotkey]").each(function(a){
		});
	});

Не понравилось, тк при каждом нажатии будет делаться выборка элементов и потом перебор массива.

Есть мнение, что можно сделать менее ресурсо-ёмко:

	$("html").keydown(function(e){
		var lastGood = false;
		var $target = $(event.target);
		var hotkey = $target.attr('hotkey')
		if(hotkey){
			// parse hotkey data and call the action
		}
	});
Ваще, я затупил конечно, мой коментарий можно проигнорировать.
вы шутитие? стою я в текстарии, нажимаю ктрл-ентер. и должен сработать хоткей не текстарии (event.target), а другого элемента — инпут.сабмит
Да, я согласен полностью, протупил же :)
Хабр уже совсем не тот. 58 комментариев и никто не сказал, что не надо изобретать атрибуты, есть же HTML5 Data.

button hotkey=«smth» — не валидно, а button data-hotkey=«smth» — вполне.

Еще можно использовать емаксовую запись хоткеев (не «Ctrl + Alt + C», а «C-M-c», например) :)

Код, который после комментария «заготовка всплыающей подсказки» — FFFFFFUUUUuuuuuu. Ну ладно, допустим, нельзя нам CSS-файлы делать, ну так блин, это же jQuery. Методы jQuery-объекта возвращают его самого, а значит можно делать так: $('.dick').css('size', 'big').css('position': 'absolute'). Пойдем дальше, откроем документацию jQuery и ВНЕЗАПНО! Можно делать так: $('.dick').css({'size': 'big', 'owner': 'developer', 'position': 'absolute'}). Да, об этом уже писали, но я не могу не повторить.
На счет data-hotkey — спасибо, не знал. Я думаю кому нужен валидный html без труда подправят.
Не стану править статью из-за этого.

А по поводу кода — это дело вкуса. Когда каждый атрибут своей строчкой удобно копипастить.

Вот у меня в phplego есть такой объект, DQ называется, работает как jquery, только для SQL.

Можно писать так:

$q = new DQ;
$admins = $q->from("mytable")->where("admin=1")->limit(10)->offset(20)->orderby('id')->objects();


Или так (будет тоже самое):
$q = new DQ;
$q->from("mytable");
$q->where("admin=1");
$q->limit(10);
$q->offset(20);
$q->orderby('id');
$admins = $q->objects();


А еще каждый метод можно вызывать статически (для ленивых):
DQ::from("mytable");
DQ::where("admin=1");
DQ::limit(10);
DQ::offset(20);
DQ::orderby('id');
$admins = DQ::objects();


Или так (для мега ленивых):
$admins = DQ::from("mytable")->where("admin=1")->limit(10)->offset(20)->orderby('id')->objects();


Очень удобно, если кому-то интересно, могу описать его в отдельной статье.
это называется Query Builder. Штука очень прикольная, на самом деле. Есть, например в Kohana и в Zend. Очень классная штука.
Я вам подскажу прикольный и лёгкий метод для него — page($number, $offset) — алиас для limit+offset, но он подставляет такие значения limit, которые должны быть для страницы $number, если на страницу будет $offset элементов. Например, page(4, 20) == "LIMIT 60, 20"

Одним из преимуществ кверибилдера — не нужно экранировать значения. Но такой интерфейс, как у вас в where не позволяет делать это. Обычно оно записывается так:
$admins = DQ::from("mytable")->where("admin", 1)->page(2, 10)->orderby('id')->objects();
Sign up to leave a comment.