Một ca khó về JavaScript, jQuery liên quan hiển thị menu popup khi hover

Chào mọi người,

Hôm nay gặp một ca quá khó bó tay nên đành lên kêu cứu anh chị em cô dì chú bác trợ giúp xem liệu có giải quyết được không chứ dân tay ngang tự vọc code như mình xem ra :cry: :cry: :cry:

Đoạn code nằm bên dưới bài này hoặc xem thực địa tại đây: https://codepen.io/superthin/pen/dypwdjr

Nào, hãy cùng xúm nhau giúp mình mỗi người một tay với nhé.

Cám ơn tất cả mọi người rất nhiều!

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Case khó về JavaScript, jQuery</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
* {
  box-sizing: border-box;
}
body {
  margin: 0;
  padding: 0;
  background-color: grey;
  min-height: 100%;
}

.main {
  margin: 0;
  padding: 0;
  width: 100%;
  font-size: 16px; 
  text-align: center;
}

.sidenav {
  height: 350px;
  position: fixed;
  z-index: 1;
  top: 50%;
  left: 1px;
  padding-left: 0.5em;
  transform: translateY(-50%);
  background-color: none;
}

.sidenav img {
  width: 32px;
  height: auto;
}

.menu-icon {
  margin: 12px 0;
  display: block;
}

.legend {
  text-align: left;
  padding: 0 2em;
}

.menu-div {
  position: fixed;
  background-color: yellow;
  display: none;
  z-index: 2;
}

.menu-div ul, .menu-div ul, .menu-div ul {
  list-style-type: none;
  margin: 0;
  padding: 0 1em;
}

.menu-div ul > li, .menu-div ul > li, .menu-div ul > li{
  line-height: 1.5;
}

.menu-div ul > li > a, .menu-div ul > li > a, .menu-div ul > li > a {
  font-size: 14px;
}

.menu-div ul > li > a:hover, .menu-div ul > li > a:hover, .menu-div ul > li > a:hover {
  color: red;
}

.canh-giua {
  text-align: center;
}
.canh-deu {
  padding:10px 250px;
  text-align: justify;
}

</style>
<script src="https://code.jquery.com/jquery-3.5.1.min.js"
        integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0="
        crossorigin="anonymous"></script>
</head>

<body>

<div class="sidenav">
  <a href="/"><img id="icon1" class="menu-icon" src="https://uphinh.vn/images/2021/01/16/c04a27c1f1227314c7033f07427b11a5.png"></a>
  <a href=""><img id="icon2" class="menu-icon" src="https://uphinh.vn/images/2021/01/16/7ca20451c12bd5729db624cb30aa3043.png"></a>
  <a href=""><img id="icon3" class="menu-icon" src="https://uphinh.vn/images/2021/01/16/bcc35872b9c669f4c90ca14000aaf26d.png"></a>
</div>
<div class="main">
  <h2>Mô tả vấn đề</h2>
  <p class="canh-deu">Khi rà chuột vào icon bên trái sẽ xuất hiện menu tương ứng với icon đó. Ta tạm gọi các icon theo thứ tự là icon1,
  icon2, icon3 (cũng là id của các icon này trên trang HTML). 3 icon này có chung class là menu-icon được gán cho thẻ img hiển thị các icon này. Các menu có id lần lượt menu1, menu2, menu3 và có chung class là menu-div</p>
  <p class="canh-deu">Vì lý do tiết kiệm click chuột nên menu <b>hoạt động ổn</b> sẽ phải thoả mãn như sau:<br>
    - Khi di chuyển trỏ chuột vào một icon thì menu hiển thị lên ngay bên cạnh<br>
    - Khi di chuyển trỏ chuột ra khỏi icon thì menu không được biến mất ngay vì như vậy thì ta không chọn được mục trên menu. Nhưng menu phải biến khi vị trí con trỏ không nằm trên menu và cũng không còn nằm trên icon<br>
    - Khi di chuyển trỏ chuột ra khỏi menu thì menu phải biến mất chứ không được nằm vô tư lự trên màn hình<br>
    - Menu tương ứng với mỗi icon chỉ hiện duy nhất chứ không được cùng xuất hiện hoặc chồng lấp lên nhau
  </p>
  <p class="canh-deu">
    Loay hoay 1 hồi thì được như các bạn đang thấy, rõ ràng đã chạy nhưng vẫn có vấn đề nếu ta thử di chuyển chuột qua lại giữa các icon. Trục trặc thường hay ở chỗ di chuyển chuột qua lại icon2 và icon3, có lúc menu không xuất hiện hoặc 2 menu chồng lấn lên nhau. 
Có vẻ như là hàm setInterval và clearInterval góp phần làm ra, thử chỉnh mili giây khác nhau vẫn chưa được. Nếu không dùng 2 hàm này thì mình chưa 
tìm ra cách nào khác để không phải click chuột mà chỉ di chuyển như hiện tại. Debug thế nào cũng vẫn 
chưa ra sau khi console.log(blah blah) để kiểm tra.</p>
  <p class="canh-deu">Hiện tại không biết làm sao giải quyết nên lên Dạy Nhậu Học kêu cứu các bạn trợ giúp.<br>
    Mình đang dùng jQuery, nhưng nếu các bạn dùng JavaScript thuần tuý mà giải quyết được các vấn đề lag khó chịu thì càng tốt.
  </p>
  <p class="canh-deu">Xin chân thành cám ơn!</p>

</div>

<div id="menu1" class="menu-div">
  <p class="canh-giua"><img src="https://uphinh.vn/images/2021/01/16/c04a27c1f1227314c7033f07427b11a5.png"></p>
  <ul>
    <li><a href="#">Menu 1 - Dong 1</a></li>
    <li><a href="#">Menu 1 - Dong 2</a></li>
    <li><a href="#">Menu 1 - Dong 3</a></li>
    <li><a href="#">Menu 1 - Dong 4</a></li>
    <li><a href="#">Menu 1 - Dong 5</a></li>
  </ul>
</div>
<div id="menu2" class="menu-div">
  <p class="canh-giua"><img src="https://uphinh.vn/images/2021/01/16/7ca20451c12bd5729db624cb30aa3043.png"></p>
  <ul>
    <li><a href="#">Menu 2 - Dong 1</a></li>
    <li><a href="#">Menu 2 - Dong 2</a></li>
    <li><a href="#">Menu 2 - Dong 3</a></li>
    <li><a href="#">Menu 2 - Dong 4</a></li>
    <li><a href="#">Menu 2 - Dong 5</a></li>
  </ul>
</div>
<div id="menu3" class="menu-div">
  <p class="canh-giua"><img src="https://uphinh.vn/images/2021/01/16/bcc35872b9c669f4c90ca14000aaf26d.png"></p>
  <ul>
    <li><a href="#">Menu 3 - Dong 1</a></li>
    <li><a href="#">Menu 3 - Dong 2</a></li>
    <li><a href="#">Menu 3 - Dong 3</a></li>
    <li><a href="#">Menu 3 - Dong 4</a></li>
    <li><a href="#">Menu 3 - Dong 5</a></li>
  </ul>
</div>
<script>
  var x;
  var y;
  var intervalFlag;

  function setMenuPos(id) {
    $('#'+id).css('left', x);
    $('#'+id).css('top', y);
  }

  function showMenu(id) {
    setMenuPos(id);
    $('#' + id).show();
  }

  function hideMenu(id) {
    intervalFlag = setInterval(
      function() {
        trueHide(id);      
      }, 250
    );
  }

  function trueHide(id) {
    if ($('#'+id+':hover').length == 0) {
      $('#'+id).hide();
      clearInterval(intervalFlag);
    }
  }

  function getMenuID(obj) {
    let idtmp = $(obj).attr('id');
    let parts = idtmp.split('-');
    let id = parts[0];
    if (id == 'icon1') return 'menu1';
    if (id == 'icon2') return 'menu2';
    if (id == 'icon3') return 'menu3';
  }
  window.onload = function() {
    
    $(document).on('mousemove', function(event) {
      x = event.clientX + 'px';
      y = event.clientY + 'px';
    });

    $('.menu-icon').on('mouseenter', function(){
      let id = getMenuID($(this));
      $('.menu-div').hide(); // prevent others remain on screen
      showMenu(id);
    });

    $('.menu-icon').on('mouseleave', function(){
      let id = getMenuID($(this));
      hideMenu(id);
    });

    $('.menu-div').on('mouseleave', function(){ 
      $(this).hide();
    });

  } // windows.onload
  
</script>

</body>
</html>
2 Likes

Bài này hình như CSS 3 cũng làm được.
Đang điện thoại nên chả giúp gì được.

3 Likes

Đổi setInterval -> setTimeoutclearInterval -> clearTimeout là cơ bản giải quyết được vấn đề.
PS: Hiện tại mình thấy 1 vấn đề nhỏ về vị trí hiển thị menu đang khá là khó chịu do nó theo vị trí con trỏ lúc rê chuột vào icon, bạn chỉ nên hiển thị theo trục y của con trỏ thôi, còn trục x thì nên cố định né vị trí của icon để tránh đè lên icon

3 Likes

Mình cũng đồng ý như thế, tại sao phải dùng jquery nhỉ
ĐÔI KHI CẮM ĐẦU NGỒI CODE LÀ CÁCH … NGU NHẤT ĐỂ GIẢI QUYẾT VẤN ĐỀ

 <div class="side-menu-sesson">
    <a href="">
        <img id="icon3" class="menu-icon"
            src="https://uphinh.vn/images/2021/01/16/bcc35872b9c669f4c90ca14000aaf26d.png">
    </a>
    
    <div id="menu3" class="menu-div">
        <p class="canh-giua"><img src="https://uphinh.vn/images/2021/01/16/bcc35872b9c669f4c90ca14000aaf26d.png"></p>
        <ul>
            <li><a href="#">Menu 3 - Dong 1</a></li>
            <li><a href="#">Menu 3 - Dong 2</a></li>
            <li><a href="#">Menu 3 - Dong 3</a></li>
            <li><a href="#">Menu 3 - Dong 4</a></li>
            <li><a href="#">Menu 3 - Dong 5</a></li>
        </ul>
    </div>
</div>
.side-menu-sesson:hover > .menu-div{
    display: block;
}
.side-menu-sesson{
    position: relative;
}
.menu-div {
    position: absolute;
    top: 0;
    left: 100%;
    background-color: yellow;
    width: max-content;
    display: none;
    z-index: 2;
}

.sidenav {
    /*height: 350px;*/
    position: fixed;
    z-index: 1;
    top: 50%;
    left: 1px;
    padding-left: 0.5em;
    transform: translateY(-50%);
    background-color: none;
}

Ngoài cách dùng css như trên có thể dùng jquery

/* Bỏ dòng này
.side-menu-sesson:hover > .menu-div{
    display: block;
}
*/
$('.side-menu-sesson').on('mouseenter', function(){
  $(this).children('.menu-div').show()
})

$('.side-menu-sesson').on('mouseleave', function(){
  $(this).children('.menu-div').hide()
})
3 Likes

Chân thành gửi lời cám ơn các bạn đã tham gia cứu bồ!

Các bạn trẻ giải quyết vấn đề nhanh gọn và sáng tạo hơn người có tuổi rất nhiều. Sau đây là cái mình học được sau khi đọc 2 bài bên trên:

Mình theo cách này và đã thấy ổn hơn ban đầu, nói chung là đã chạy được, dù còn hơi lag so với cách làm chỉ thuần CSS bên dưới.

Gợi ý việc hiển thị chỉ lấy toạ độ trục y, còn x né ra để không đè lên icon nữa rất tuyệt, tạo sự dễ chịu hơn khi rê chuột vào. Gút dóp ờ mấy zìng!

2 ý kiến trên rất có lý, tuyệt vời, phù hợp với các trình duyệt hiện đại.

Trước đó mình cũng đang suy nghĩ tìm cách xem cài đặt kiểu gì trong trường hợp hiện đang nhưng tối mù. Mình vẫn chưa làm chủ được mấy cái như pseudo-class, pseudo-element trong CSS vì học CSS tự phát, mang tính chắp vá, chưa đạt được mức upper-intermediate. Dù hiểu rằng dùng CSS thuần cho hiệu suất, trải nghiệm tốt hơn phối hợp với JavaScript, chứ không phải thích thú gì phải mang JavaScript vào chi cho nó rách việc ra nhưng lực bất tòng tâm nên đang… code trâu như bữa các bạn thảo luận sôi nổi.

Ở case này mình vẫn chưa hiểu được ý niệm cách làm. Đã sửa code nhận xét thấy chạy tốt, không lag như khi có JS, chỉ cần chỉnh chọc lại vị trí tí xíu, chỉnh z-index, background-color để tránh chồng lấp là ổn. Nhưng có khả năng là lại làm nó hỏng, với 1 icon có vẻ được, còn 3 icon vẫn chưa hiểu cài đặt thế nào.

Nếu @LocNguyenXuan99 có thời gian, hãy “thông não” giúp mình bằng cách giải thích “giải thuật” với nhé. Xin cám ơn bạn!

3 Likes

Nếu bạn muốn hover vào a và hiển thị b thì b nên có mối quan hệ với a.

Ở đây mình dùng quan hệ parent-> child cho dễ set position.
ngoài ra có silbling (trường hợp floating lable input chẳng hạn) nhưng phức tạp hơn.

4 Likes
83% thành viên diễn đàn không hỏi bài tập, còn bạn thì sao?