barbitoff programmer`s blog
Здесь я публикую заметки из программерской жизни: грабли, на которые мне случилось наступить, проблемы, для которых было найдено элегантное (или не очень) решение, а также все, с чем мне пришлось столкнуться и чем хотелось бы поделиться =)
PS Если хотите меня поблагодарить - на странице есть 3 места, чтобы это сделать =)
суббота, 30 апреля 2011 г.
суббота, 23 апреля 2011 г.
C# - аналог ключевого слова synchronized для методов в Java
Иногда нужно, чтобы некоторый метод объекта выполнялся в каждый момент времени только одним потоком (т.е. чтобы вызовы к данному методу объекта сериализовывались, т.е. ставились в очередь). Для такого контроля конечно можно использовать, например, семафор, но в Java было предусмотрено специальное ключевое слово - synchronized. В C# есть аналогичная конструкция, которая, правда, не является частью языка:
using System.Runtime.CompilerServices; [MethodImplAttribute(MethodImplOptions.Synchronized)] protected void someSyncMethod() { ... }Также как и в Java, синхронизированными могут быть помечены сразу несколько методов класса, при этом, т.к. при вызове любого синхронизированного метода блокируется объект-хозяин, в любой момент времени возможно будет выполнение лишь одного из них.
в
08:39:00
0
коммент.
Отправить по электронной почтеНаписать об этом в блогеПоделиться в XОпубликовать в FacebookПоделиться в Pinterest
Ярлыки:
C#,
C# аналог synchronized в Java,
C# синхронизированные методы,
C# synchronized,
Java,
Java synchronized,
MethodImplAttribute,
MethodImplOptions.Synchronized
воскресенье, 17 апреля 2011 г.
Настройка mysql-сервера на работу с cp1251 по умолчанию
В my.ini (/etc/mysql/my.cnf в Linux) прописать слудующее (в блоке [mysqld]:
character-set-server=cp1251
collation-server=cp1251_general_ci
Или соотв. параметрами командной строки.
Настроить клиент mysql на данный character set можно опцией тамже, но в блоке [mysql]:
default-character-set=cp1251
Просмотреть текущие параметры collation (соединение / БД / сервер) можно запросом:
SHOW VARIABLES LIKE 'colla%'
character-set-server=cp1251
collation-server=cp1251_general_ci
Или соотв. параметрами командной строки.
Настроить клиент mysql на данный character set можно опцией тамже, но в блоке [mysql]:
default-character-set=cp1251
Просмотреть текущие параметры collation (соединение / БД / сервер) можно запросом:
SHOW VARIABLES LIKE 'colla%'
суббота, 16 апреля 2011 г.
Связь серверов БД в MySQL
Столкнулся с проблемой поиска в MySQL аналога db-link в Oracle / linked server в MS SQL Server, т.е. возможностью в одном запросе / процедуре использовать как локальную БД, так и удаленную.
В MySQL эта задача решена следующим образом (в версиях, начиная с 5.0):
1) Во-первых, в опциях mysql (my.ini) необходимо убрать "skip-federated" и добавить "federated" (или просто запускать mysqld с соотв. ключом).
2) На локальном сервере создается таблица, по структуре аналогичная удаленной, но в качестве движка используется FEDERATED (т.е. делаем на удаленной базе SHOW CREATE TABLE `xxx`, после чего заменяем STORAGE ENGINE на FEDERATED). Также к SQL-запросу создания таблицы нужно добавить параметр CONNECTION="mysql://user_name[:password]@host_name[:port_num]/db_name/tbl_name". Получается запрос вроде:
Если таблиц, подключающих к одной и той же удаленной БД, много, может быть удобнее создать связанный сервер командой вида:
Пока поддерживаются только MySQL удаленные сервера. Транзакции также не поддерживаются на должно уровне.
Подробнее почитать можно в документации:
http://dev.mysql.com/doc/refman/5.6/en/federated-storage-engine.html
http://dev.mysql.com/doc/refman/5.6/en/create-server.html
В MySQL эта задача решена следующим образом (в версиях, начиная с 5.0):
1) Во-первых, в опциях mysql (my.ini) необходимо убрать "skip-federated" и добавить "federated" (или просто запускать mysqld с соотв. ключом).
2) На локальном сервере создается таблица, по структуре аналогичная удаленной, но в качестве движка используется FEDERATED (т.е. делаем на удаленной базе SHOW CREATE TABLE `xxx`, после чего заменяем STORAGE ENGINE на FEDERATED). Также к SQL-запросу создания таблицы нужно добавить параметр CONNECTION="mysql://user_name[:password]@host_name[:port_num]/db_name/tbl_name". Получается запрос вроде:
CREATE TABLE federated_table ( id INT(20) NOT NULL AUTO_INCREMENT, name VARCHAR(32) NOT NULL DEFAULT '', other INT(20) NOT NULL DEFAULT '0', PRIMARY KEY (id), INDEX name (name), INDEX other_key (other) ) ENGINE=FEDERATED DEFAULT CHARSET=latin1 CONNECTION='mysql://fed_user@remote_host:9306/federated/test_table';3) Всё, запускаем скрипт, после чего все манипуляции с данной таблицей (кроме DDL) ведут к манипуляцием с удаленной таблицей, хотя по всех запросах она будет как-будто локальная.
Если таблиц, подключающих к одной и той же удаленной БД, много, может быть удобнее создать связанный сервер командой вида:
CREATE SERVER s FOREIGN DATA WRAPPER mysql OPTIONS (USER 'Remote', HOST '192.168.1.106', DATABASE 'test');Тогда в параметре CONNECTION можно будет указать только имя сервера (в данном приере - 's'). Сам этот вариант не пробовал (не возникало необходимости), не совсем пока понимаю как в данном случае задать имя удаленной таблицы.
Пока поддерживаются только MySQL удаленные сервера. Транзакции также не поддерживаются на должно уровне.
Подробнее почитать можно в документации:
http://dev.mysql.com/doc/refman/5.6/en/federated-storage-engine.html
http://dev.mysql.com/doc/refman/5.6/en/create-server.html
пятница, 15 апреля 2011 г.
undefined reference to 'timer_settime'
Проблема:
При компиляции программы, работающей с таймером, линковщик выдает ошибки типа:
undefined reference to 'timer_create'
undefined reference to 'timer_settime'
Решение: при компиляции использовать ключ -lrt
Вот примерчик их использования для периодической генерации SIGALRM:
При компиляции программы, работающей с таймером, линковщик выдает ошибки типа:
undefined reference to 'timer_create'
undefined reference to 'timer_settime'
Решение: при компиляции использовать ключ -lrt
Вот примерчик их использования для периодической генерации SIGALRM:
#include <signal.h>
#include <sys/time.h>
#include <time.h>
...
timer_t mytimer;
struct sigevent timer_event;
/*
* - use signals
* - send SIGALRM
*/
timer_event.sigev_notify=SIGEV_SIGNAL;
timer_event.sigev_signo= SIGALRM;
if (timer_create(CLOCK_REALTIME, &timer_event, &mytimer) < 0)
{
printf("timer create error");
exit(0);
}
if(timer_settime(mytimer,CLOCK_REALTIME,&timerSettings,NULL)!=0)
{
printf("timer setting error");
exit(0);
}
while(1)
pause();
пятница, 1 апреля 2011 г.
Рисование дуги окружности в ActionScript
Встроенных средст рисования дуг в ActionScript нет, поэтому пришлось написать свою функцию:
protected function drawArc(gr:Graphics,centerX:Number,centerY:Number,R:Number, startAngle:Number, endAngle:Number, angleStep:Number=1):void { var xc : Number, yc : Number; xc = centerX+R*Math.sin(startAngle*Math.PI/180); yc = centerY-R*Math.cos(startAngle*Math.PI/180); gr.moveTo(xc,yc); for (var i:Number=startAngle+angleStep; i<=endAngle; i+=angleStep) { xc = centerX+R*Math.sin(i*Math.PI/180); yc = centerY-R*Math.cos(i*Math.PI/180); gr.lineTo(xc,yc); } }
Форматирование отображения чисел с плавающей точкой в ActonScript
Не нашел я в ActionScript аналога С-шного / php-шного sprintf для того, чтобы задать параметры отображения числа с плавающей точкой (Number) при преобразовании в строку.. Зато у Number есть метод Number.toFixed(p), преобразующий его в число с фиксированной запятой, где p - число разрядов после запятой.
urlencode / urldecode в ActionScript
Функции ActionScript, аналогичные вышеуказанным - escape() и unescape().
Аналог php`шного var_dump() для Flash / ActionScript
Нашел очень полезную функцию для получения дампа любых объектов в ActionScript, типа var_dump() в php. Используется она очень просто:
Сам класс можно скачать тут: http://www.flexer.info/2008/06/25/dump-debug-method-like-var_dump-function-in-php-and-debug-class/ или тут: http://narod.ru/disk/360902001/var_dump_sources.zip.html
import com.flexer.Debug; ... Debug.dump(someObj) ;
Сам класс можно скачать тут: http://www.flexer.info/2008/06/25/dump-debug-method-like-var_dump-function-in-php-and-debug-class/ или тут: http://narod.ru/disk/360902001/var_dump_sources.zip.html
Обновление изображения с помощью JavaScript
Иногда бывает нужно обновить динамически генерируемое изображение с сервера (например, капчу). Это можно сделать, добавив к адресу изображения рандомное число:
$("#someDynamicImg").attr('src', $("#someDynamicImg").attr('src')+"?"+Math.random());В случае, если в адресе изображения уже есть строка поиска (как в капче), вместо "?" нужно поставить "&"
Сравнение годов в MySQL
Наткнулся тут на очень опасную особенность MySQL (может правда это свойственно не только ей и вообще является нормальным поведением, но для меня оно стало неожиданным):
В одной из таблиц базы есть поле типа YEAR. Так получилось, что в него попал год "0000", и из админки (написанной на php) я решил его удалить. Вводимый в поле год для удаления intval`ился на стороне сервера, т.е. моё желание удалить год "0000" превратилось в желание удалить год "0". Какое же было мое удивление, когда вместо одной строки, из БД удалилось 800+ строк, и причем строка с годом "0000" осталась на месте. А удалились строки с 2000 годом.
Действительно, SELECT * FROM `table` WHERE `year` = '0' выдает строки с 2000 годом, а ни как не с нулевым. Это связано с тем, что при проведении выборок или присваиваний значений полям числа 0-69 преобразуются в 2000-2069, а 70-99 - в 1970-1999.
В одной из таблиц базы есть поле типа YEAR. Так получилось, что в него попал год "0000", и из админки (написанной на php) я решил его удалить. Вводимый в поле год для удаления intval`ился на стороне сервера, т.е. моё желание удалить год "0000" превратилось в желание удалить год "0". Какое же было мое удивление, когда вместо одной строки, из БД удалилось 800+ строк, и причем строка с годом "0000" осталась на месте. А удалились строки с 2000 годом.
Действительно, SELECT * FROM `table` WHERE `year` = '0' выдает строки с 2000 годом, а ни как не с нулевым. Это связано с тем, что при проведении выборок или присваиваний значений полям числа 0-69 преобразуются в 2000-2069, а 70-99 - в 1970-1999.
Часто моргает лампочка на приемнике Logitech cordless rumblepad 2
Проблема такая: на ресивере часто моргает лампочка, сам геймпад работает (реагирует на вкл/выкл вибрации), винда тоже норм его видит, вот только на нажатия кнопок на паде не реагирует. Перезагрузка / перестановка драйверов не помогает.
Решение: вынуть из геймпада 1 аккум, зажать кнопки 5 и 7, и, не от пуская, вставить аккум обратно. И всё работает)
Решение: вынуть из геймпада 1 аккум, зажать кнопки 5 и 7, и, не от пуская, вставить аккум обратно. И всё работает)
Ubuntu и щелчки винчестера на ноутбуке
Поставил Ubuntu 10.10 Desktop на нетбук Asus EEEPC T101MT и столкнулся со следующей проблемой: при питании от батареи винчестер с периодичностью раз в 20-40 сек издает странные звуки (типа щелчков), как будто все время останавливается, паркует головки, а потом снова раскручивается. При этом под родной Win7 все в порядке. Понятно, что дело это нехорошее, Load Cycle Count в SMART винчестера растёт с пугающей скоростью (на момент решения проблемы он составил 1010), да и звуки неприятные. Снятие галочки "замедлять винчестеры по необходимости" в "Управлении питанием" ничего не изменило.
Решилась проблема следующим образом: в /lib/hdparm/hdparm-functions заменить:
С этим значением вообще-то можно поэкспериментировать, найдя оптимальное между энергопотреблением и износом винта.
Решилась проблема следующим образом: в /lib/hdparm/hdparm-functions заменить:
строка 82/254на:
hdparm_set_option -B128
hdparm_set_option -B255Все, щелчки прекращаются. Вообще, возникают они из-за т.н. Advanced Power Managment винчестера, которые включается убунтой при переходе на питание от батареи. Значение по-умолчанию (128) по-разному воспринимается разными винтами, видимо мой WD`шка его поняла совсем экстремально.
С этим значением вообще-то можно поэкспериментировать, найдя оптимальное между энергопотреблением и износом винта.
Настройка D-Link DIR-615 для Beeline
Промучался пол дня с настройкой DIR-615 под билайн, а все вот почему: оказалось, что адрес L2TP сервера надо прописывать не как доменное имя (tp.internet.beeline.ru), а в виде IP, иначе роутер сервера не видет. Почему, не знаю, но оно так (через nslookup сервера с компа тоже почему-то не видно, а вот пингом он пингуется, оттуда я и узнал его IP).
Рабочие настройки под питерский Билайн приведены ниже:
Посмотреть на Яндекс.Фотках
Рабочие настройки под питерский Билайн приведены ниже:
Посмотреть на Яндекс.Фотках
Политика безопасности JavaScript и локальные IP
В соответствии с политикой безопасности JS запросы (XMLHttpRequest) на локальные адреса (например, в подсети 192.168.x.0) запрещены (если только сам сайт, с которого идет запрос, не локален). Причем никакой ошибки не отображается (по крайней мере при использовании jQuery, даже через метод .ajax с указанием обработчика ошибок), просто запроса не происходит (по крайней мере в Опере тишина наблюдается даже в консоли ошибок JS; как дела обстоят в FF не в курсе, т.к. меня на тот момент, когда я столкнулся с данной проблемой, интересовала Opera с её UserJS).
document.domain и префикс "www"
По крайней мере по мнению Opera, www.mysite.ru и mysite.ru являются РАЗНЫМИ доменами, со всеми вытекающими (например, запрещается доступ из одного окна / фрейма в родительское / дочернее). Странно, но факт, приходится считаться, иначе вылазит что-нить такое:
ReferenceError: Security error: attempted to read protected variable
Работа с cookies в JavaScript
2 функции, облегчающие чтение / запись cookies:
function setCookie (name, value, expires, path, domain, secure) { document.cookie = name + "=" + escape(value) + ((expires) ? "; expires=" + expires : "") + ((path) ? "; path=" + path : "") + ((domain) ? "; domain=" + domain : "") + ((secure) ? "; secure" : ""); } function getCookie(name) { var cookie = " " + document.cookie; var search = " " + name + "="; var setStr = null; var offset = 0; var end = 0; if (cookie.length > 0) { offset = cookie.indexOf(search); if (offset != -1) { offset += search.length; end = cookie.indexOf(";", offset) if (end == -1) { end = cookie.length; } setStr = unescape(cookie.substring(offset, end)); } } return(setStr); }
Отключение кэширования HTML-страницы
<meta http-equiv="Cache-Control" content="no-cache, no-store, max-age=0, must-revalidate"/> <meta http-equiv="Pragma" content="no-cache"/> <meta http-equiv="Expires" content="Fri, 01 Jan 1990 00:00:00 GMT"/>
Выполнение JSONP-запросов в jQuery
JSONP - полезный подход для выполнения кроссдоменных асинхронных запросов, позволяющий обойти изместно ограницение XMLHTTPRequest. Смысл его заключается в добавлении на страницу тега <script>, загружающего данные формата JSON со стороннего домена:
<script src="/some/crossdomain/url/here" type="text/javascript"></script>
Полученный код должен содержать нечто вроде:
var data = {one : "12", two : "34"} someCallback(data);
, где someCallback() - функция, которая должна обработать полученный JSON.
Чтобы получатель запроса знал, какую именно функцию написать вместо someCallback, имя функции должно передаваться в тексте запроса, например: URL_запроса?callback=myCallBack.
Но это всё теория, и если пользоваться jQuery, всё оказывается намного проще:
$.getJSON(crossdomainRequestUrl, function(data) { ... })
, где crossdomainRequestUrl должен заканчиваться на "&callback=?". Конечный знак вопроса укажет jQuery, что выполняется JSONP зарпос. При этом имя callback-функции указывать не нужно. Полученный JSON будет передан, как обычно, в обработчик, заданный вторым параметром $.getJSON. На серверной части значение параметра callback, которое будет сформировано jQuery самостоятельно, нужно исопльзовать как имя callback функции, вызов которой нужно вставить в выдаваемый в ответ JS-скрипт.
IE и Array.indexOf()
В IE, вплоть до 8 версии, нет метода indexOf для объектов Array. Чтож, пусть это будет на совести индусов из Microsoft, придется жить без него, ну или добавлять в свои скрипты такой фикс:
if(!Array.indexOf){ Array.prototype.indexOf = function(obj){ for(var i=0; i<this.length; i++){ if(this[i]==obj){ return i; } } return -1; } }
Получение текстового содержимого узла в IE
Во всех "приличных" браузерах у узла есть свойство node.textContent. В ИЕ (вплоть до ИЕ8) это не работает, поэтому приходится делать так:
nextDiv.firstChild.nodeValue (если текстовый узел - первый, или вообще единственный). Иначе - можно использовать следующую функцию:
В принципе, её можно порядком укоротить, убрав ненужные case. Ну или использовать $(node).text() из jQuery ;)
nextDiv.firstChild.nodeValue (если текстовый узел - первый, или вообще единственный). Иначе - можно использовать следующую функцию:
function getElemText(node){ return node.text || node.textContent || (function(node){ var _result = ""; var childrens = node.childNodes; var i = 0; while (i < childrens.length) { var child = childrens.item(i); switch (child.nodeType) { case 1: // ELEMENT_NODE case 5: // ENTITY_REFERENCE_NODE _result += arguments.callee(child); break; case 3: // TEXT_NODE case 2: // ATTRIBUTE_NODE case 4: // CDATA_SECTION_NODE _result += child.nodeValue; break; case 6: // ENTITY_NODE case 7: // PROCESSING_INSTRUCTION_NODE case 8: // COMMENT_NODE case 9: // DOCUMENT_NODE case 10: // DOCUMENT_TYPE_NODE case 11: // DOCUMENT_FRAGMENT_NODE case 12: // NOTATION_NODE // skip break; } i++; } return _result; }(node)); }
В принципе, её можно порядком укоротить, убрав ненужные case. Ну или использовать $(node).text() из jQuery ;)
Отмена submit формы по нажатию Enter (работает везде, даже в Opera!)
Бывает нужно отменить submit формы по нажатию клавиши Enter, когда фокус находится, например, в текстовом поле (мне это понадобилось при реализации всплывающих подсказок ввода). Видел в интернете множество решений, некоторые из которых пугали своей сложностью или работали не во всех браузерах. В итоге остановился на таком варианте: добавить этому полю обработчики keyup, keydown и keypress следующего вида:
Возможно, немного избыточно, но точно работает в IE8, FF 3.6.8 и Opera 10.6, и, в отличие от других вариантов, не требует модифицировать html код формы, выносить кнопку submit за пределы формы, и делать прочие телодвижения, особенно противопоказанные, если Вы пишите библиотечное решение, а не одну единственную страницу.
Если добавить только один обработчик keydown, перестает работать в Опере.
Один минус - не работает при отключенном JavaScript, но это, по-моему, свойственно всем решениям - при отключенном JS либо остается работать submit по Enter, либо пропадает возможность submit`a вообще,что, на мой взгляд, куда опаснее.
function(event){ event = event || window.event; // если Enter - обработать if(event.keyCode==13) { // отменить действия по-умолчанию для Enter, остановить всплытие event.preventDefault ? event.preventDefault() : (event.returnValue=false); event.stopPropagation ? event.stopPropagation() : (event.cancelBubble = true); return false; } }
Возможно, немного избыточно, но точно работает в IE8, FF 3.6.8 и Opera 10.6, и, в отличие от других вариантов, не требует модифицировать html код формы, выносить кнопку submit за пределы формы, и делать прочие телодвижения, особенно противопоказанные, если Вы пишите библиотечное решение, а не одну единственную страницу.
Если добавить только один обработчик keydown, перестает работать в Опере.
Один минус - не работает при отключенном JavaScript, но это, по-моему, свойственно всем решениям - при отключенном JS либо остается работать submit по Enter, либо пропадает возможность submit`a вообще,что, на мой взгляд, куда опаснее.
Аналог php-функции trim на JavaScript
trimStr(s) { s = s.replace( /^\s+/g, ''); return s.replace( /\s+$/g, ''); }
Запуск NetBeans под Linux с английским языком
При установке NetBeans из репозиториев Ubuntu он встает с русским языком интерфейса, что лично мне не очень удобно, т.к. привык под виндой работать с английским, и от перевода немного коробит.
Для того, чтобы запустить NetBeans с английским языком интерфейса, нужно в командной строке задать:
/usr/bin/netbeans --locale en:US
(ну или создать Launcher c такой командной строкой)
Изменение положения и порядка кнопок Свернуть/Восстановить/Закрыть в Gnome
После обновления на Ubuntu 10.4 у многих кнопки управления окном съехали в левую часть заголовка окна (сделано это было разработчиками Ubuntu намеренно, не знаю правда, из каких соображений). У меня правда после обновления этого не произошло, однако недавно они туда уехали самопроизвольно. Работать стало дико не удобно (привыкнуть можно конечно, но хотелось сделать как было, а не привыкать), поэтому (спасибо MagicLover`у - http://magiclover.ru/ubuntu.html) я нашел как вернуть все на свои места. В командной строке надо набрать:
sudo apt-get install gconf-editor
gconf-editor
После этого выбрать apps ->metacity ->general ->button layout, где задать что-то вроде "menu:minimize,maximize,close". Здесь двоеточие разделяет левый и правый край заголовка окна. menu - это кнопка меню в левой част окна, ну а с остальными наверное понятно. Ещё можно написать в какои-либо месте "spacer", тогда в соотв. промежутке между кнопками появится расстояние.
Главное не запускать gconf-editor из-под sudo (как советует MagicLover), иначе настройки будут менятся для рута, а не для текущего пользователя.
Кроссбраузерное затемнение экрана на CSS+jQuery
Кроссбраузерно.
CSS:
#TB_overlay { position: fixed; z-index: 3; /* при необходимости можно поменять на другой */ top: 0; left: 0; height: 100%; width: 100%; background-color: #000; filter:progid:DXImageTransform.Microsoft.Alpha(opacity=55); -moz-opacity: 0.55; -khtml-opacity: 0.55; opacity: 0.55; } * html #TB_overlay { position: absolute; height: expression(document.body.scrollHeight > document.body.offsetHeight ? document.body.scrollHeight : document.body.offsetHeight + "px"); }
Java Script:
if(!jQuery("body").find("#TB_overlay").is("div")) /* если фон уже добавлен не добавляем повторно */ { if(!jQuery.browser.msie) /* если браузер не ИЕ фоном будет div */ jQuery("body").append("<div id='TB_overlay'></div>"); else /* иначе добавляем iframe */ jQuery("body").append("<div id='TB_overlay'><iframe scrolling='no' frameborder='0' style='position: absolute; top: 0; left: 0; width: 100%; height: 100%; filter:alpha(opacity=0)'></iframe></div>"); } $("#TB_overlay").fadeIn("fast");
Получение GET параметров запроса из JavaScript
function getParam(sParamName)
{
var Params = location.search.substring(1).split("&"); // отсекаем "?" и вносим переменные и их значения в массив
var variable = "";
for (var i = 0; i < Params.length; i++)
{ // пробегаем весь массив
if (Params[i].split("=")[0] == sParamName)
{ // если это искомая переменная
// если значение параметра задано, то возвращаем его
if (Params[i].split("=").length > 1) variable = Params[i].split("=")[1];
return variable;
}
}
return "";
}
Работа с iframe из JavaScript
Получить доступ к документу iframe`a можно следующим образом (работает в FF, Opera, IE7):
var frameDocument=document.getElementById("iframeID").contentWindow.document;
К полученному frameDocument можно применять все методы документа, например, getElementById.
Для того, чтобы из родительской страницы отслеживать перезагрузку iframe`a, можно повесить на него onLoad:
<iframe onLoad="someFunc()" ... ></iframe>
someFunc() будет вызываться при каждой новой загрузке содержимого фрейма.
Центрирование div-блоков
Центрирование div блоков во всех приличных браузерах делается следующим образом:
<div style="margin-left: auto; margin-right: auto; width: 50%;">
В ИЕ как всегда все через (_)(_), поэтом приходится делать так:
<div style="text-align:center;"> <div style="width: 50%;"> Содержимое блока DIV </div> </div>
В итоге универсальный вариант центрирования div-блока выглядит вот так:
<div style="text-align: center;"> <div style="width: 50%; margin-left: auto; margin-right: auto; "> Содержимое блока DIV </div> </div>
Подписаться на:
Сообщения (Atom)