Элементы рядом с границей — как избежать двойной рамки — html/css

Элементы рядом с границей — как избежать двойной рамки — html/css

Так и не смог придумать адекватный заголовок для этой статьи. Поэтому просто опишу суть проблемы. Если честно, первый раз столкнулся с такой проблемой и даже не знал как ее решить. Но решение было найдено и я решил поделиться им (вдруг кому-нибудь пригодится, да и самому не забыть).

Проблема в том, что надо сделать меню. Как на картинке. Количество пунктов заранее неизвестно. Каждый пункт имеет рамку. Но выделенный пункт имеет рамку другого цвета. Вроде все просто? Но при разборе возникают проблемы. Если просто задать рамку для всех элементов, то рядом стоящие покажут двойную рамку, что очень некрасиво. Использовать фиксированный размер, наложение или отрицательные отступы - не комильфо, нужно идти дальше. Если убрать у всех элементов правую границу, а у последнего ее отобразить (:last-child), затем у элемента, который следует после выделенного поменять левую границу - тоже сойдет. Но что делать, если выделенный элемент последний или единственный? Читаем дальше.


Не буду писать теорию, сразу покажу на практике. Для этого мы создадим такое меню. Начинаем с разметки:

<ul>
	<li>Текст</li>
	<li>Текст</li>
	<li>Текст</li>
	<li>Текст</li>
	<li class="active">Текст</li>
</ul>

Это обычный маркированный список с пятью элементами. Представим, что это меню. Последнему элементу я добавил класс active, чтобы мы рассмотрели сразу самый сложный случай.

Теперь напишем стили:

ul {
	margin:0;
	padding:0;
	list-style:none;
}
ul li {
	float:left;
	width:100px;
	height:30px;
	line-height:30px;
	border:1px solid red;
	border-right:none;
	text-align:center;
	cursor:pointer;
}
ul li:last-child {
	border-right:1px solid red;
}
ul li.active {
	border-color:black;
}
ul li.active + li {
	border-left-color:black;
}

Что мы делаем в стилях? Настраиваем отображение элементов и размер (идут друг за другом в одну линию, размер 100x30). Далее задаем красную границу border:1px solid red; и тут же убираем правую границу border-right:none; (чтобы не получалось двойной рамки). Но теперь у последнего элемента не будет правой границы, зададим ее - ul li:last-child {border-right:1px solid red;}. Прекрасно. Теперь переходим к выделенному элементу с классом active, который должен обладать рамкой черного цвета.

Задаем ему черный цвет рамки border-color:black;. Теперь маленькая хитрость - каждому последующему элементу за выделенным, мы укажем, чтобы он левую границу окрашивал в черный цвет - ul li.active + li {border-left-color:black;}.

Вот и все. Все будет работать корректно. Но почему тогда, когда элемент стоит последним, то у него правая граница черная? Ведь мы задавали ее красной? Тоже самое касается вопроса, если элемент единственный в списке. Тут играет свою роль понятие вес стиля. Стиль ul li.active {border-color:black;} переопределяет стиль ul li:last-child {border-right:1px solid red;}, потому что имеет больший вес.

Полный код страниц (для действия демки я написал небольшой скрипт, который присваивает класс active разным элементам по клику):

<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script>
(function($) {
  $(function() {
 
    $('ul').delegate('li:not(.active)', 'click', function() {
      $(this).addClass('active').siblings().removeClass('active');
	  })
 
  })
})(jQuery)
</script>
<style>
ul {
	margin:0;
	padding:0;
	list-style:none;
}
ul li {
	float:left;
	width:100px;
	height:30px;
	line-height:30px;
	border:1px solid red;
	border-right:none;
	text-align:center;
	cursor:pointer;
}
ul li:last-child {
	border-right:1px solid red;
}
ul li.active {
	border-color:black;
}
ul li.active + li {
	border-left-color:black;
}
</style>
</head>
 <body>
	<ul>
		<li>Текст</li>
		<li>Текст</li>
		<li>Текст</li>
		<li>Текст</li>
		<li class="active">Текст</li>
	</ul>
</body>
</html>

Вот и все. Проверьте в демке.


27.03.17
Для просмотра сайта обновите браузер.