В процессе работы над интерфейсом одного продукта, появилась надобность в изготовлении собственного дизайна кнопок. За это время код, который заменяет стандартную кнопку на требуемую несколько раз переписывался и в данный момент тоже далёк от идеала. Учитывая все текущие проблемы кросс-браузерности, за это время выяснились и получилось нижеописанное.
Допустим, что она должна выглядеть примерно так:
Кнопка должна:
На первом этапе я попробовал максимально минимизировать код, создавая подобную кнопку двумя контейнерами один из которых вложен во второй со смещениями для отображения красивого бэкграунда с закруглениями, в конце концов, этого не достаточно для того что бы в процессе была возможность наследовать свойства и методы самой кнопки, то есть сама кнопка как элемент должна присутствовать внутри контейнеров (<span><div><button/></div></span>).
Для упрощения эксперимента используем jQuery,
Спустя некоторое время выяснилось, что крайне сложно под IE, FF, Opera контролировать размеры и внешний вид всех контейнеров с кнопкой вкупе c помощью обычных CSS. Результат превратился в борьбу хаков над стилями. Тут же я впервые столкнулся с надобностью написания различных стилей для FF2 и FF3.
В конце концов за основу была взята концепция, используемая в ExtJS, а именно — таблица.
[левый край|кнопка|правый край]
В связи с этим, количество вопросов с позиционированием значительно сократилось, и вместе с ним — количество CSS.
Допустим, что исходные кнопки задается следующим образом
<input type='submit' value='Submit' class='replaceMe w100'>
<input type='button' value='Pushme' class='replaceMe ico ico-image' disabled='disabled'>
В данном случае у нас получается что класс replaceMe будет являтся селектором для замены, класс w100 — вспомогательный класс (допустим, отвечающий за ширину кнопки), который должен будет наследоваться. ico и ico-image — классы отвечающие за иконки.
Картинка-спрайт для прототипа:
CSS прототипа:
И собственно, сам код:
Большое спасибо за внимание. Надеюсь что знания почерпнутые мной в процессе работы пригодились.
update: Демонстрация работы, по просьбам трудящихся.
Допустим, что она должна выглядеть примерно так:
Кнопка должна:
- быть inline-блоком
- иметь возможность наследовать стилизацию родителя (в пределах возможностей)
- Наследовать некоторые свойства и методы родителя.
На первом этапе я попробовал максимально минимизировать код, создавая подобную кнопку двумя контейнерами один из которых вложен во второй со смещениями для отображения красивого бэкграунда с закруглениями, в конце концов, этого не достаточно для того что бы в процессе была возможность наследовать свойства и методы самой кнопки, то есть сама кнопка как элемент должна присутствовать внутри контейнеров (<span><div><button/></div></span>).
Для упрощения эксперимента используем jQuery,
Спустя некоторое время выяснилось, что крайне сложно под IE, FF, Opera контролировать размеры и внешний вид всех контейнеров с кнопкой вкупе c помощью обычных CSS. Результат превратился в борьбу хаков над стилями. Тут же я впервые столкнулся с надобностью написания различных стилей для FF2 и FF3.
В конце концов за основу была взята концепция, используемая в ExtJS, а именно — таблица.
[левый край|кнопка|правый край]
В связи с этим, количество вопросов с позиционированием значительно сократилось, и вместе с ним — количество CSS.
Чит-коды
Допустим, что исходные кнопки задается следующим образом
<input type='submit' value='Submit' class='replaceMe w100'>
<input type='button' value='Pushme' class='replaceMe ico ico-image' disabled='disabled'>
В данном случае у нас получается что класс replaceMe будет являтся селектором для замены, класс w100 — вспомогательный класс (допустим, отвечающий за ширину кнопки), который должен будет наследоваться. ico и ico-image — классы отвечающие за иконки.
Картинка-спрайт для прототипа:
CSS прототипа:
.ico {
padding-left: 20px !important;
padding-bottom: 1px !important;
background-position: 0px 0px;
background-repeat: no-repeat;
}
.ico-image { background-image: url("page_tick.gif") !important;}
/* Перебиваем все настройки которые могли бы помешать дизайну свыше*/
.xBtn tr td {
border: 0 none !important;
border-bottom: 0 none !important;
padding: 0;
font:normal 11px sans-serif, tahoma, verdana, helvetica ;
height: 21px;
min-height: 21px;
}
/* Стиль таблицы */
.xBtn {
cursor:pointer;
border-collapse: collapse;
white-space: nowrap;
display: inline;
width: auto;
}
/* Стиль кнопки внутри центральной ячейки. Убираем все возможные отступы, пробелы.
* Имеем ввиду, что у IE есть такая странность: Добавлять лишние пробелы по краям кнопки,
* нечто походящее на padding: 0 1.3em;
* */
.xBtn button {
border:0 none;
background:transparent no-repeat;
font:normal 11px tahoma,verdana,helvetica;
padding-left:3px;
padding-right:3px;
height: 21px;
cursor:pointer;
margin:0;
overflow:visible;
width:auto;
-moz-outline:0 none;
outline:0 none;
}
/* Допольнение для IE */
* html .xBtn button {width: 1px;}
*+html .xBtn button {width: 1px;}
*+html .xBtn button {padding-top:3px;}
/* если кнопка будет с иконкой */
.xBtn .xBtn-text-ico {
background-position: 0 0px;
background-repeat: no-repeat;
height: 16px;
padding: 0 0 2px 18px;
margin-top: 1px;
}
/* А возможно это будет просто текст */
.xBtn .xBtn-text {
background-position: 0 0px;
background-repeat: no-repeat;
padding-top:0px;
padding-bottom:2px;
padding-right:0;
margin-top: 1px;
height: 16px;
}
/* борьба с IE */
*+html .xBtn .xBtn-text { padding-top:1px; margin-top: 2px;}
.xBtn-Left, .xBtn-Right {
font-size:1px;
line-height:1px;
width:3px;
height:21px;
}
.xBtn-Left { background: url(btn-sprite.png) no-repeat 0 0;}
.xBtn-Right { background: url(btn-sprite.png) no-repeat 0 -21px;}
.xBtn .xBtn-Left i, .xBtn .xBtn-Right i {
display:block;
width:3px;
overflow:hidden;
font-size:1px;
line-height:1px;
}
.xBtn .xBtn-Center {
background:url(btn-sprite.png) repeat-x 0 -42px;
vertical-align: middle;
text-align:center;
cursor:pointer;
white-space:nowrap;
}
.xBtn-over .xBtn-Left{ background: url(btn-sprite.png) repeat-x 0 -63px !important; }
.xBtn-over .xBtn-Right{ background: url(btn-sprite.png) repeat-x 0 -84px !important; }
.xBtn-over .xBtn-Center { background: url(btn-sprite.png) repeat-x 0 -105px !important;}
.xBtn-click .xBtn-Left { background: url(btn-sprite.png) repeat-x 0 -126px !important;}
.xBtn-click .xBtn-Right { background: url(btn-sprite.png) repeat-x 0 -147px !important; }
.xBtn-click .xBtn-Center { background: url(btn-sprite.png) repeat-x 0 -168px !important; }
.xBtn em {
font-style:normal;
font-weight:normal;
height: 16px;
}
* This source code was highlighted with Source Code Highlighter.
И собственно, сам код:
$('.replaceMe').each(function(){
// у исходника тут же убираем уже ненужный класс
$(this).removeClass('replaceMe');
// Создаем таблицу
var BtnTable = document.createElement('table');
var BtnTableRow = BtnTable.insertRow(0);
var LeftBtnCell = BtnTableRow.insertCell(0);
var CenterBtnCell = BtnTableRow.insertCell(1);
var RightBtnCell = BtnTableRow.insertCell(2);
// Что бы ячейки таблицы небыли пустыми,
// создаем для них какие-нибуть контролируемые элементы DOM
var newBtnContainer = document.createElement('em');
var newBtnSideLContainer = document.createElement('i');
var newBtnSideRContainer = document.createElement('i');
// Назначаем классы, вставляем в контейнеры
$(LeftBtnCell).addClass('xBtn-Left').append(newBtnSideLContainer);
$(RightBtnCell).addClass('xBtn-Right').append(newBtnSideRContainer);
// Замечательный атрибут,
// предотвращающий выделение текстовой информации внутри блока
newBtnContainer.setAttribute('uselectable', 'on');
$(BtnTable)
// Назначаем класс для самой таблицы-кнопки
.addClass('xBtn')
// Переносим из исходной кнопки ее значение в атрибут Title, для красоты эксперимента
.attr('title', $(this).attr('value') || '')
// Изменение классов при наведении и клике на таблице-кнопке
.hover(
function(){
if ($('button:enabled', $(BtnTable)).length) $(this).addClass('xBtn-over');
},
function(){
$(this).removeClass('xBtn-over');
$(this).removeClass('xBtn-click');
}
)
.mousedown(function(){
//$(newBtn).focus();
$(this).addClass('xBtn-click');
})
.mouseup(function(){
$(this).removeClass('xBtn-click');
});
// Будем считать, что иконки на кнопках будут задаваться двумя классами
// первый из которых будет отвечать за место под кнопку, второй - за само изображение.
// ico ico-image
// Определяем, есть ли у нас иконка, и что за...
var xBtnClasses = this.className.split(' ');
var hasIco = $(this).hasClass('ico');
var icoClassName = '';
for (var i = 0; i < xBtnClasses.length; i++ ) {
if (xBtnClasses[i].toString().match(/ico-\w+/)) icoClassName = xBtnClasses[i].toString();
}
// Убираем из исходника классы отвечающие за иконки.
if (hasIco && icoClassName) {
$(this).removeClass('ico').removeClass(icoClassName)
}
// Копируем в центральную ячейку с будущей кнопкой классы из исходника (оставшиеся классы)
// Обозначаем ее собственным классом и вставляем в нее контейнер для будущей кнопки
$(CenterBtnCell).append(newBtnContainer).addClass(this.className).addClass('xBtn-Center');;
// Копируем событие onclick исходника
var onClickEv = $(this).attr('onclick');
// Если есть какое событие, то устанавливаем его на всю таблицу-кнопку
if (jQuery.isFunction(onClickEv)) {
$(BtnTable).bind('click', function(e){
onClickEv();
});
// Если нет события, то может быть наш родитель был submit-ом?
// Нажимаем на кнопку и ждем спецэффектов
} else if ( this.type == 'submit' ) {
$(BtnTable).bind('click', function(e){
if ($(this).find('button').length) {
var f = $(this).find('button')[0];
f.click();
}
});
}
// Скрываем нашего прородителя и вставляем перед ним прототип нашей кнопки
$(this).hide().before(BtnTable);
// Вместо кода написанного ниже сначала была попытка создать кнопку методами JavaScript.
// Но в связи с существующей проблемой под IE, которая запрещает изменять атрибут type
// кнопку создаем обычным способом:
var Btn = '<button ' +
// Трансплантируем тип кнопки
'type="' + ($(this).attr('type') || 'button') + '" ' +
// Назначаем ID исходника
'id="' + this.id + '" ' +
// Передаем классы: иконок, если есть,
'class="' + ((hasIco && icoClassName) ? 'xBtn-text-ico ico ' + icoClassName : 'xBtn-text') + (($(this).attr('disabled')) ? ' disabled"' : '') + '" ' +
// Передаем остальные аттрибуты
(($(this).attr('disabled')) ? 'disabled="disabled"' : '') +
'name="' + $(this).attr('name') + '" ' +
'title="' + $(this).attr('title') + '" ' +
'style="' + (($(this).attr('value') == '') ? 'width:16px;' : '') + '" ' +
'>' + $(this).attr('value') +
'</button>';
// Tadaaaa!
newBtnContainer.innerHTML = Btn;
// Небольшие жонглирования с шириной под IE, котрый не совсем понимает, что такое ширина, когда нет содержимого текста
if ($.browser.msie && $(this).attr('value') != '') {
if ($(CenterBtnCell).width()) {
$(CenterBtnCell).find('button').css('width', $(CenterBtnCell).width() + 'px');
} else {
$(CenterBtnCell).find('button').css('width', TextMetrixWidth(this) + 18 + 'px');
}
}
$(this).remove();
});
* This source code was highlighted with Source Code Highlighter.
Большое спасибо за внимание. Надеюсь что знания почерпнутые мной в процессе работы пригодились.
update: Демонстрация работы, по просьбам трудящихся.