Showing posts with label CSS. Show all posts
Showing posts with label CSS. Show all posts

Saturday, June 11, 2016

Detecting CSS position: sticky support

If you're using modernizr for other things, by all means use that. If you just need a simple check to see if the current browser supports position: sticky, insert this javascript code:

var positionStickySupport = function() {
 var el = document.createElement('a'),
     mStyle = el.style;
 mStyle.cssText = "position:sticky;position:-webkit-sticky;position:-ms-sticky;";
 return mStyle.position.indexOf('sticky')!==-1;
}();

positionStickySupport will be true if it's supported. You should use the following CSS:


.myElement {
 position: -webkit-sticky;
 position: -ms-sticky;
 position: sticky;
 top: 0px;
}

Of course you can set top however you want. If you're wondering, the -ms-sticky is for future-proofing. As of this writing, Microsoft plans to support the feature in Edge, but it's not clear whether they'll use a prefix or not. Safari uses -webkit. Opera has it implemented in pre-release form without a prefix, and Firefox already supports it without one.

It would have been possible to use the CSS.supports() functionality, but Safari 6-8 support position: sticky but not CSS.supports().

Here is the one-liner version:

var positionStickySupport = function() {var el=document.createElement('a'),mStyle=el.style;mStyle.cssText="position:sticky;position:-webkit-sticky;position:-ms-sticky;";return mStyle.position.indexOf('sticky')!==-1;}();

Wednesday, May 11, 2016

Touch-enabled swipeable scroll bars

Looking for an elegant, functional scrollbar solution for the web?

If you google around, you'll find TouchSwipe and various other Javascript-based solutions. These, while impressive, are trying too hard. They all have issues, either with handling links within the scrollable area, or with stutters in the animation, or with unnatural (on iOS anyway) non-bouncy ending of the scroll. I had several issues I could never overcome, not to mention their large code size and complexity.

You want something that works on touch devices and non-touch devices and is flexible and easy. I found the solution to be CSS-based, and this is it. It works on any modern browser and IE 9+.



The key is that you need an enclosing container (scrollWrapper) with overflow hidden, and an internal container (scrollableArea) that scrolls within it, and is allowed to scroll using the native facilities.

Normally, on a desktop, using the native scroll facilities would mean that you'd get an ugly scrollbar, so you need to hide that, and this can be done in CSS. But you need to replace that functionality for non-touch users. That means adding buttons on the ends that can move the scrollable area (and could also mean enabling the mouse wheel). The buttons call javascript functionw that simply update the scroll position and allows CSS transitions to occur. On the codepen below I even include a tiny javascript plugin that enables scrolling horizontally with a mousewheel (vertically would be automatic).

The result is something that's totally natural and totally flexible, and actually much simpler than the javascript solutions.

You can see a demo of the resulting scrollbar here

The HTML structure is trivial:

<div class="scrollWrapper">
  <a class="scrollBtn prev">&lt;</a>
  <div class="scrollableArea">
    <!-- stuff to scroll here -->  
  </div>
  <a class="scrollBtn next">&gt;</a>
</div>

The key part is this simple CSS code:
div.scrollWrapper {
 width: 96%;
 height: 91px;
 margin-left: 2%;
 position: relative;
 overflow: hidden;
 white-space: nowrap;
}
div.scrollableArea {
 position: relative;
 width: 100%;
 height: 125%; /* crop scrollbar (if applicable) outside scrollWrapper, while maintaining scrollability */
 white-space: nowrap;
 overflow-x: scroll;
 overflow-y: hidden;
 -webkit-overflow-scrolling:touch;
}

Note that the height is 91 in order to fit 81px-high divs with 5px margin on top and bottom. The scrollableArea height needs to be about 20px higher than the scrollWrapper to ensure that the desktop scrollbar will be cropped off.

Sample CSS for the scroll buttons:
.scrollBtn {
  display: inline-block;
  position: absolute;
  margin-top: 5px;
  margin-bottom: 5px;
  top: 0px;
  height: 81px;
  width: 30px;
  line-height: 81px;
  text-align: center;
  vertical-align: middle;
  background-color: #444;
  opacity: 0.5;
  text-decoration: none;
  z-index: 100;
  font-size: 30px;
  font-weight: 700;
  outline: none;
  cursor: pointer;
}
.scrollBtn:hover {
  opacity: 0.7;
  transition: 0.3s;
}
.scrollBtn.prev {
  left: 0px;
}
.scrollBtn.next {
  right: 0px;
}


Finally, here's javascript to handle the buttons and (optionally) enable horizontal scrolling (using jQuery):
$('a.scrollBtn.prev').click(function(e) {
 var scroller = $(".scrollableArea");
 scroller.animate({scrollLeft: scroller.scrollLeft() - (scroller.innerWidth() - 91)});
 e.preventDefault();
});

$('a.scrollBtn.next').click(function(e) {
 var scroller = $(".scrollableArea");
 scroller.animate({scrollLeft: scroller.scrollLeft() + (scroller.innerWidth() - 91)});
 e.preventDefault();
});

/** 
  * Enable scrolling an element horizontally using up/down mousewheel events
  * (when over the element). Amount is the number of pixels to move per
  * wheel turn (default 120) 
  **/
$.fn.hScroll = function (amount) {
 amount = amount || 120;
 $(this).bind("DOMMouseScroll mousewheel", function (event) {
  var origEvent = event.originalEvent, direction = origEvent.detail ? origEvent.detail * -amount : origEvent.wheelDelta, position = $(this).scrollLeft();
  position += direction > 0 ? -amount : amount;
  $(this).scrollLeft(position);
  event.preventDefault();
 })
};
$('.scrollableArea').hScroll(70);




Wednesday, August 26, 2015

Fancy, Semantic tooltips with CSS only

I came across this post describing a way of allowing control over the appearance of tooltips in a semantic way that avoids too much extra markup and doesn't trip up screen readers. Really good idea.

Tooltip shown when hovering over the first really

I made a couple modifications to allow it to work with a href tags as well as non-link spans, eg:

<p>I <a href="#" data-tt="I mean really, really" class="tt">really</a> like pie.</p>

or

<p>I <span data-tt="I mean really, really" class="tt">really</span> like pie.</p>

I also spiced it up some and made it so it doesn't appear immediately, but only after hovering for a bit, and then transitions in gracefully.

Finally, since tooltips may be enclosed in a container with overflow hidden, or near the right edge of the viewport, I added some code to choose the positioning for each tooltip (lower left, lower right, upper left, or the default: upper right). When set appropriately, this ensures they can be seen.

Best of all it seems to be supported by essentially all browsers, including back to at least IE6.

Here's the CSS:

.tt {
  position: relative;
  cursor: pointer;
  outline: 0;
}
.tt:after {
  content: attr(data-tt);
  position: absolute;
  left: 1.7em;
  top: -1.7em;
  white-space: nowrap;
  color: #FFF;
  background: rgba(0, 0, 0, 0.8);
  padding: 3px 7px;
  border-radius: 3px;
  -moz-border-radius: 3px;
  -webkit-border-radius: 3px;
  margin-left: 7px;
  border: 1px solid gray;
  margin-top: -3px;
  transition-delay: 0s;
  visibility: hidden;
  opacity:0.0;
}
.tt:hover:after {
  visibility:visible;
  opacity: 1.0;
  transition-delay: .5s;
  -webkit-transition-delay: .5s;
  transition-duration: .4s;
  -webkit-transition-duration: .4s;
  transition-timing-function: ease-in;
  -webkit-transition-timing-function: ease-in;
}
.tt-lr:after {
  top: 1.7em;  
}
.tt-ll:after {
  top: 1.7em;
  left: auto;
  right: 1.7em;
}
.tt-ul:after {
  left: auto;
  right: 1.7em;
}


You can see it in action here