Angular.JS ngRepeat and Bootstrap layout bug on IE7
We have an Angular.JS application which is also using Bootstrap for its layout, and we've come across an irritating layout bug on IE7 (for which the client still has a requirement - albeit with a "please upgrade your browser if you can" notice).
Supporting IE7 with any modern web framework is a bit of a pain.
First, we made sure that we followed all of the Angular guidelines for using IE7.
We made sure we included HTML5 shim and JSON3, plus Respond for responsiveness, and *almost* everything was rendering correctly, except for a horizontal row of elements produced by an ngRepeat element, which were too wide for the space, and wrapped underneath one another.
Looking closely, we observed that the first element had some unnecessary left margin. This stems from an IE7 bug that has long been fixed in IE8 and above, relating to the :firstChild
pseudo class.
When Angular injects the relevant elements into the DOM to fulfil your ngRepeat request, it also injects a comment, like this:
On all other browsers, this comment is ignored when it comes to the pseudo-class :firstChild. On IE7 it is not, so your first actual element does not match the selector, and the css is not applied.
Sadly, Bootstrap uses this selector to set the left margin on that element to 0, to ensure the layout fits correctly.
Fortunately, Angular comes to the rescue in the form of $index
This gives us the zero-based index of the current iteration of the repeat. We can use this to add custom classes for our first and last elements.
<div ng-repeat="page in pages" ng-class="getPageClass($index)">
// Do our stuff...
</div>
In our controller, getPageClass($index)
builds the class list, adding our custom first and last classes when appropriate.
$scope.getPageClass = function($index) {
var result = "span" + Math.max(1, Math.floor(12 / $scope.pageSize()));
if ($index == 0) {
result += " firstChildForIE7";
}
if ($index == $scope.pages.length - 1) {
result += " lastChildForIE7";
}
return result;
};
And we can then target it with some custom styles.
.firstChildForIE7 {
margin-left: 0 !important;
}
.lastChildForIE7 {
margin-right: 0 !important;
}