0

I'm quite new with css. I want hold the ul visible when hovering from parent to ul. I don't know how do it.

HTML Markup

<drop-down class="dropdown">
    <span>Dropdown menu<i class="fa fa-cog"></i></span>
    <ul>
        <li>
            <a href="#">Github<i class="fa fa-github"></i></a>
        </li>
        <li>
            <a href="#">BitBucket<i class="fa fa-bitbucket"></i></a>
        </li>
        <li>
            <a href="#">Dropbox<i class="fa fa-dropbox"></i></a>
        </li>
        <li>
            <a href="#">Google drive<i class="fa fa-google"></i></a>
        </li>
    </ul>
</drop-down>

CSS

drop-down {
    background-color: #e9e9e9;
    border: 1px solid #d2c2c2;
    border-radius: 2px;
    display: flex;
    flex-flow: column nowrap;
    height: 40px;
    justify-content: center;
    position: relative;
    width: 160px;
}
drop-down:hover { cursor: pointer; }
drop-down > span {
    align-items: center;
    color: #555;
    display: flex;
    font-family: 'segoe ui';
    font-size: .9rem;
    justify-content: space-between;
    padding: 0px .75rem;
    pointer-events: none;
}
drop-down > span > i {
    color: inherit;
}
drop-down ul {
    background-color: #e9e9e9;
    border: 1px solid #d2c2c2;
    border-radius: 2px;
    box-shadow: 0px 2px 5px 1px rgba(0,0,0,.15);
    display: block;
    right: 10%;
    list-style: none;
    padding: .5rem 0;
    position: absolute;
    opacity: 0;
    pointer-events: none;
    visibility: hidden;
    top: 160%;
    transition: all .2s ease-out;
    width: 100%;
    z-index: 999;
}
drop-down ul > li {
    color: #555;
    display: block;
}
drop-down ul > li:hover {
    background-color: #007095;
    color: rgba(255,255,255,.9);
}
drop-down ul > li > a {
    align-items: center;
    color: inherit;
    display: flex;
    font-family: 'segoe ui';
    font-size: .95rem;
    justify-content: space-between;
    padding: .5rem .75rem;
    text-decoration: none;
}
drop-down ul > li > a > i {
    color: inherit;
}
drop-down:focus {
    outline: none;
}
drop-down:hover ul {
    pointer-events: auto;
    opacity: 1;
    top: 120%;
    visibility: visible;
}

You can see it running at this fiddle: http://jsfiddle.net/vt1y9ruo/1/

I can do it with javascript, but I don't want use it for something small.

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Rex Garcia
  • 83
  • 9
  • 1
    The way it is built, you can't do it with only CSS. The `hover` event is working as it's supposed to according to your instructions: once you "leave" the button, you're not on `hover` state and your list disappears. If you don't want to use javascript, you can't have your list with `position: absolute;`, you have to make it's position relative to the parent, and adjust the top position so that they don't have a "gap" between eachother. – Cthulhu Jun 17 '15 at 14:20

4 Answers4

4

Here's a working fiddle: http://jsfiddle.net/vt1y9ruo/8/

It works by inserting an invisible bridge between the button and the list.

drop-down:hover ul, #ulwrap:hover ul {
    pointer-events: auto;
    opacity: 1;
    top:120%;
    visibility: visible;
}
#ulwrap {
    display: block;
    height:0;
    width:100%;
    position:absolute;
}
drop-down:hover #ulwrap, #ulwrap:hover {
    height:100px;
}
JBux
  • 1,394
  • 8
  • 17
  • 1
    If you need to have the gap, this is the correct answer. It's not very elegant, but it works. – Cthulhu Jun 17 '15 at 14:34
  • 1
    Not sure how it isn't elegant, this is exactly how to do it: hover over the parent, to reveal one of the childnodes. Though, I suggest JavaScript if it needs to work on mobile. – Steven Lacks Jun 17 '15 at 14:58
1

if you want to do this using the hover feature of css, the gap between the button and the list is what's killing you. either remove this gap or use js

on a side note there is no harm in using js for something small, this is what its used for, just make it nice and reusable

bruceyyy
  • 409
  • 3
  • 14
0

Well, pure css solution (many thanks @JBux) is a little dirty (mark up). I finally go for JS solution and for this, created a custom tag:

const helper = new Helper(); // helper functions
var ddProto = Object.create(HTMLElement.prototype);
ddProto.properties = {
    list: null,
    options: null,
    value: null,
    icon: null,
    index: -1,
};
ddProto.initEvents = function() {
    var self = this;
    // mouse over button
    this.addEventListener('mouseover', function(e) {
        if(!helper.hasClass(this, 'dropdown-active'))
            helper.addClass(this, 'dropdown-active');
    });
    // mouseleave over button
    this.addEventListener('mouseleave', function(e){
        var rect = this.getBoundingClientRect();
        var left = e.pageX;
        var bottom = e.pageY;
        // if mouse is out of X axis of button and if mouse is
        // out (only of top) of Y axis of button, hide ul
        if(left < rect.left || left >= rect.right || bottom < rect.top) {
            helper.delClass(this, 'dropdown-active');
        }
    });
    // list loses hover
    this.properties.list.addEventListener('mouseleave', function(e) {
        if(helper.hasClass(self, 'dropdown-active'))
            helper.delClass(self, 'dropdown-active');
    });
    // elements click
    [].forEach.call(this.properties.options, function(e) {
        e.addEventListener('click', function(event){
            event.preventDefault();
            // set the text of selected value to button
            helper.text(self.properties.value, e.innerText);
            // set the position of selected value
            self.properties.index = helper.position(e.parentNode);
            // set the <i> class name to the button (fontawesome)
            self.properties.icon.className = this.children[0].className;
            // hide ul
            helper.delClass(self,'dropdown-active');
        },true);
    });
};
ddProto.value = function() {
    return this.properties.value;
};
ddProto.index = function() {
    return this.properties.index;
}
ddProto.createdCallback = function() {
    this.properties.list = this.querySelector('ul');
    this.properties.options = this.querySelectorAll('ul > li > a');
    this.properties.value = this.querySelector('span');
    this.properties.icon = this.querySelector('span > i');
    this.initEvents();
};
document.registerElement('drop-down', {prototype: ddProto});

Working fiddle: http://jsfiddle.net/m2dtmr24/2/

Thank you so much.

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Rex Garcia
  • 83
  • 9
-1

The thing you could check is the + selector (more here)

In short it lets you add styles to elements right next to each other. The actual css might look something like this:

.dropdown{
  display: none;
}    
.button:hover+.dropdown{
  display: block;
}

This will only work when .dropdown is directly below .button in the DOM

The animation might be harder, but you could achieve something similar by for example using transition on opacity, and toggle opacity instead of display

Community
  • 1
  • 1
Piotr Kruczek
  • 2,384
  • 11
  • 18