自定义上下文菜单始终可见

时间:2019-02-12 17:31:52

标签: javascript jquery html css css3

我正在实现一个自定义菜单,当用户单击鼠标左键时会出现该菜单,并且尝试设置菜单的位置(X,Y)时遇到麻烦,因此无论使用哪个菜单,整个菜单都可见页面的一部分打开。

下面的图像代表了问题:

a busy cat

菜单的宽度随其文本的增加而增加,因此调整其位置和高度也是一个挑战。

var elements = $('#content').find('h1, p, span');

var setMenuPosition = function(x, y) {
  $("#menu").css('top', y);
  $("#menu").css('left', x);
};

var setSelectedText = function() {
  $('#menu').data('text', $(this).text());
};

var openMenu = function(e) {
  e.stopPropagation();
  elements.css('border', '1px solid transparent');
  $(this).css('border', '1px dashed #333');
  $('#menu').addClass('active');
  $('#selected-text').text($('#menu').data('text'));
  setMenuPosition(e.pageX, e.pageY);
};

var closeMenu = function() {
  elements.css('border', '1px solid transparent');
  $('#menu').removeClass('active');
};

$('#content').find('h1, p, span').on('mouseenter', setSelectedText);
$('#content').find('h1, p, span').on("click", openMenu);
$('#menu').on('mouseleave', closeMenu);
h1,
p,
span {
  border: 1px solid transparent;
}

#content {
  background-color: #e9e9ea;
  padding: 25px;
}

#menu {
  visibility: hidden;
  opacity: 0;
  transition: visibility 0s, opacity 0.5s linear;
  background-color: #84ce6a;
  color: #fff;
  padding: 15px;
  position: absolute;
  min-width: 200px;
  border-radius: 8px;
}

#menu.active {
  visibility: visible;
  opacity: 1;
}

#my-span {
  background-color: rgb(255, 79, 79);
  color: rgb(255, 255, 255);
  padding: 0px 5px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="content">
  <h1>My Title</h1>
  <p>My text</p>
  <p>My another text</p>
  <p>My text <span id="my-span">My span</span>, other part of the same text</p>
</div>

<div id="menu">
  <h4>Selected text is: <span id="selected-text"></span></h4>
  <button>
    Ok
    </button>
</div>

我的小提琴:https://jsfiddle.net/robsonnogueira/295d78ak/

2 个答案:

答案 0 :(得分:1)

要防止菜单移出视口,您需要以下逻辑:

// 1. Set menu content
$menuContent.text(ev.currentTarget.textContent);

// 2. Get X, Y click coordinates
let X = ev.clientX;
let Y = ev.clientY;

// 3. Fix X, Y
X = Math.max(0, Math.min(X, $win.width() - $menu.outerWidth(true)) );
Y = Math.max(0, Math.min(Y, $win.height() - $menu.outerHeight(true)) );

// 4. Show menu
$menu.css({left:X, top:Y}).addClass('is-visible');

考虑了窗口( viewport )的大小和菜单的大小(插入其内容之后)-并通过结合{{1 }}和Math.max()

以下是一个示例:

Math.min()
jQuery($ => {

  const $win = $(window);
  const $menu = $('#menu');
  const $menuContent = $('#menu-content');
  
  const menuOpen = (ev) => {
    ev.stopPropagation();
    
    // 1. Set menu content
    $menuContent.text(ev.currentTarget.textContent);
  
    // 2. Get X, Y click coordinates
    let X = ev.clientX;
    let Y = ev.clientY;
        
    // 3. Fix X, Y
    X = Math.max(0, Math.min(X, $win.width() - $menu.outerWidth(true)) );
    Y = Math.max(0, Math.min(Y, $win.height() - $menu.outerHeight(true)) );
    
    // 4. Show menu
    $menu.css({left:X, top:Y}).addClass('is-visible');
  }
  
  const menuClose = () => {
    $menu.removeClass('is-visible');
  }

  // Events
  $(".menu-open").on('click', menuOpen);
  $(".menu-close").on('click', menuClose);
  $(document).on('click', menuClose);
  $menu.on('click', ev => ev.stopPropagation());

});
html, body {
  height: 100%;
  margin:0;
  font: 14px/1.4 sans-serif;
}

#menu {
  position: fixed;
  max-width: 300px;
  left: 0;
  top: 0;
  background: #84ce6a;
  padding: 10px 20px;
  visibility: hidden;
  opacity: 0;
  transition: visibility 0.24s, opacity 0.24s;
}

#menu.is-visible {
  visibility: visible;
  opacity: 1;
}

/*Demo only*/
.menu-open{
  position: absolute;
}
.menu-open:nth-child(1) {top: 0; left: 0;}
.menu-open:nth-child(2) {top: 0; right: 0;}
.menu-open:nth-child(3) {bottom: 0; left: 0;}
.menu-open:nth-child(4) {bottom: 0; right: 0;}

以上内容可以通过以下方式进一步改进:

  • 使菜单在“右侧” 上执行与左侧类似的操作-首先计算是否可以实现点翻转(将元素固定在相对的右上角,底部或左下角),或者像现在一样将其固定在远处。
  • 如果菜单宽度/高度不适合视口,则应将其固定(可能需要一些额外的CSS用于菜单内容/正文滚动条)

答案 1 :(得分:-1)

罗布森,有几件事情要考虑。但主要是您似乎希望弹出窗口或模式出现在同一位置,从而为显示文本提供最大的空间。我更新了代码以更改一些CSS和JS。本质上,您只想在用户单击或计时器用尽时打开和关闭“活动”类。另外,您还希望弹出窗口/模式的位置正确,这意味着一直到最左侧,因为它为文本提供了最大的空间。或者,可能需要包装,其中您可以将模式/弹出窗口的最大宽度设置为适当的尺寸。

JavaScript:在这里,我刚刚添加了一个jQuery调用,因为您使用它来切换活动类并注释掉了您设置的直接CSS值。

CSS:我将“可见性”更改为“显示”,因为它使它脱离了文档流。基本上是可见性:即使它不是“可见的”,隐藏的流中仍具有div并影响对齐。另一方面,显示会将其从流中删除,因此不影响对齐。我使用的值为“ display:none”(不可见)和“ display:block”(用于显示),因为它是一个块元素。研究其他影响的其他值。

var elements = $('#content').find('h1, p, span');

var setMenuPosition = function(x, y) {
  var m = $('#content');
  $("menu").toggleClass('active');
  //$("#menu").css('top', y);
  //$("#menu").css('left', x);
};

var setSelectedText = function() {
  $('#menu').data('text', $(this).text());
};

var openMenu = function(e) {
  e.stopPropagation();
  elements.css('border', '1px solid transparent');
  $(this).css('border', '1px dashed #333');
  $('#menu').addClass('active');
  $('#selected-text').text($('#menu').data('text'));
  setMenuPosition(e.pageX, e.pageY);
};

var closeMenu = function() {
  elements.css('border', '1px solid transparent');
  $('#menu').removeClass('active');
};

$('#content').find('h1, p, span').on('mouseenter', setSelectedText);
$('#content').find('h1, p, span').on("click", openMenu);
$('#menu').on('mouseleave', closeMenu);
h1,
	p,
	span {
	  border: 1px solid transparent;
	}

	#content {
	  background-color: #e9e9ea;
	  padding: 25px;
	}

	#menu {
	  /*visibility: hidden;*/
	  display:none;
	  opacity: 0;
	  transition: visibility 0s, opacity 0.5s linear;
	  background-color: #84ce6a;
	  color: #fff;
	  padding: 15px;
	  /*position: absolute;*/
	  position:relative;
	  top:0;
	  left:0;
	  min-width: 200px;
	  border-radius: 8px;
	}

	#menu.active {
	  /*visibility: visible;*/
	  display:block;
	  opacity: 1;
	}

	#my-span {
	  background-color: rgb(255, 79, 79);
	  color: rgb(255, 255, 255);
	  padding: 0px 5px;
	}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="content">
  <h1>My Title</h1>
  <p>My text</p>
  <p>My another text</p>
  <p>My text <span id="my-span">My span</span>, other part of the same text</p>
</div>

<div id="menu">
  <h4>Selected text is: <span id="selected-text"></span></h4>
  <button>
    Ok
    </button>
</div>