javascript上下文_使用JavaScript构建自定义右键单击(上下文)菜单

javascript上下文

随着Web的不断成熟,与仅信息网站相反的Web应用程序正得到越来越多的利用。 Web应用程序的两个最先进的领先示例是Gmail和Dropbox。 随着Web应用程序在功能,可访问性和实用性方面的不断增长,简化其效率的需求也在增加。 自定义的上下文菜单是两个前面提到的应用程序正在使用的一种新兴(且非常有用)的概念。 在本教程中,我们将介绍一些内容:

  1. 定义上下文菜单实际上是什么,并了解其在Web应用程序体系结构中的用途和位置。
  2. 使用前端代码来构建我们自己的自定义上下文菜单,从使用CSS样式到使用JavaScript触发
  3. 首先,对自定义上下文菜单的实用性进行一些讨论,在生产级实现中,有的有无。

让我们潜入吧!

什么是上下文菜单?

将从Wikipedia借用并修改一个定义 ,因为它很好地涵盖了它:

上下文菜单是GUI中的菜单,在用户交互(例如鼠标右键单击)时出现。 上下文菜单提供了一组有限的选项,这些选项在操作系统或应用程序的当前状态或上下文中可用。 通常,可用选项是与所选对象相关的动作。

在计算机上,右键单击桌面将启动操作系统的本机上下文菜单。 从这里,您可能可以创建一个新文件夹,获取一些信息以及其他操作。 使用网络浏览器时,右键单击页面将启动该浏览器的默认上下文菜单。 您将能够获取页面信息或查看源。 右键单击图像也会显示其自己的一组选项-您可以保存图像,在新选项卡中打开它,然后复制它,等等。 这些都是默认行为,但是它们确实是曾经由应用程序制造商构建的。 这里要注意的有趣事情是,可用菜单操作如何根据启动菜单的情况或上下文而变化。

Web应用程序开始部署自己的自定义上下文菜单,以向用户提供相关操作。 Dropbox和Gmail是完美的例子,可让您执行归档,删除,下载,查看等操作。 但是怎么做呢? 在Web浏览器中,当执行右键单击操作时,将触发一个事件。 此事件是contextmenu事件 。 要部署自定义的上下文菜单,我们需要阻止默认行为,然后设置,触发和放置我们自己的菜单。 这是一项工作,但我们将逐步进行。 让我们开始创建一个基本的应用程序结构,以便我们可以玩一些真实的东西。

绘制图片-任务列表应用示例

想象一下,我们正在创建一个很好的任务列表应用程序。 是的,这是一个常见的示例,您可能对听到任务列表应用程序感到厌倦,但是无论如何,我们还是要继续研究它,因为它总是可以画出清晰的画面。 在我们应用程序的登录页面上,我们很可能会列出尚未完成的任务。 我们可以单击任务以获取更多信息,我们可以添加新任务,编辑任务以及删除任务-如果需要,可以使用基本的CRUD应用。 随着近来各种设备访问Web应用程序,我们发现自己实现了许多手势和动作,以使它们更加自然。 例如,向左滑动可能会提示用户删除或存档任务。

不过,我们也正在笔记本电脑上访问相同的Web应用程序,因此我们可以合并相似的手势。 右键单击并启动上下文菜单对于用户来说是很自然的动作,通常会向用户显示一系列操作。 在Dropbox中,这些操作的范围从创建新目录到下载或上传文件。 在我们的应用程序中,让我们想象一下,通过右键单击一个任务项目来启动上下文菜单,可以使我们查看,编辑或删除该任务。 我们将以此为背景来构建菜单,并可能将一些数据传递给我们的操作。

在本教程的最后,您将在CodePen上看到一个有效的示例。 在此之前,让我们一起努力,逐步构建自己的平台,以便您可以看到进度。 我将使用一些现代CSS进行快速的结构开发,并创建一个具有一些data-*属性的虚拟任务列表。 我还将拉入Eric MeyerCSS reset ,并将所有属性的box-sizing属性重置为border-box ,如下所示:

*,
*::before,
*::after {
  box-sizing: border-box;
}

我不会在CSS样式上添加任何前缀,但是CodePen演示将利用自动前缀。 让我们开始吧!

建立我们的基础结构

我们将正常打开HTML文档,并添加标题,主要内容区域,虚拟任务列表和页脚。 我会加入Font Awesome以获得一些额外的视觉愉悦,并且我会想一想。 我将确保每个任务都包含一个data-id属性,该属性实际上是从某种数据库中生成的。 每个任务还​​将有一个“动作”部分,因为我们希望动作尽可能地易于访问。 请记住,上下文菜单提供了一种向用户展示操作的独特方法。 这是标记的重要部分:

<body>
  <ul class="tasks">
    <li class="task" data-id="3">
      <div class="task__content">
        Go To Grocery
      </div>
      <div class="task__actions">
        <i class="fa fa-eye"></i>
        <i class="fa fa-edit"></i>
        <i class="fa fa-times"></i>
      </div>
    </li>
    <!-- more task items here... -->
  </ul>
  <script src="main.js"></script>
</body>

记住,您可以签出CodePen演示以及完整的文档结构。 现在让我们看一下CSS。 如果您在CodePen中工作,则可以在设置窗格中轻松添加自动前缀和CSS重置。 如果不是,则必须手动添加这些内容,或者根据您的开发环境设置任务运行程序。 我还加入了Roboto字体家族,并且如上所述,我包括了Font Awesome。 请记住,此演示的重点是上下文菜单的创建,因此图标操作指示器和添加任务按钮将无法使用。 这是到目前为止CSS的相关部分:

/* tasks */

.tasks {
  list-style: none;
  margin: 0;
  padding: 0;
}

.task {
  display: flex;
  justify-content: space-between;
  padding: 12px 0;
  border-bottom: solid 1px #dfdfdf;
}

.task:last-child {
  border-bottom: none;
}

同样,您可以从CodePen演示中获取全套样式。 我们只在这里重点介绍重要部分。 好了,现在我们有一个基本的小操场可以在其中工作。 让我们开始考虑实际的上下文菜单!

开始我们的自定义上下文菜单-标记

我们的菜单将像当今大多数菜单一样开始-嵌套在nav标签内的无序列表。 菜单内的每个动作将是带有链接的列表项。 每个链接将负责特定的操作。 如前所述,我们希望上下文菜单负责以下三个操作:

  1. 查看任务
  2. 编辑任务
  3. 删除任务

让我们这样标记上下文菜单:

<nav class="context-menu">
  <ul class="context-menu__items">
    <li class="context-menu__item">
      <a href="#" class="context-menu__link">
        <i class="fa fa-eye"></i> View Task
      </a>
    </li>
    <li class="context-menu__item">
      <a href="#" class="context-menu__link">
        <i class="fa fa-edit"></i> Edit Task
      </a>
    </li>
    <li class="context-menu__item">
      <a href="#" class="context-menu__link">
        <i class="fa fa-times"></i> Delete Task
      </a>
    </li>
  </ul>
</nav>

如果您想在哪里放置此标记,让我们暂时将其留在结束body标签之前。 这是有原因的,在我们继续讲CSS之前,让我们注意几件事:

  1. 我们最终希望上下文菜单显示在我们单击鼠标右键的任何位置,这意味着它应该绝对定位,并且不会出现在文档的其余部分之外。 我们不希望它显示在任何会弄乱放置坐标的相对容器中,因此我将其留在文档底部的原因。
  2. 我们最终将希望某些变量或属性与上下文菜单相关联。 例如,当我们单击“删除任务”时,我们将要知道要删除的任务,而只有首先选择右键单击哪个任务,我们才知道这一点。

让我们继续CSS。

设置自定义菜单样式-CSS

简而言之,我们知道我们希望我们的菜单处于绝对位置。 除此之外,让我们添加一些额外的样式以使其看起来像样。 我还将z-index设置为10,但是请记住,在您的应用程序中,您希望菜单位于所有内容的顶部,因此请相应地设置z-index 。 在演示和源代码中,您会发现很多额外的样式,可以带来一些美感,但是这里要注意的重要一点是上下文菜单的定位和z-indexing:

.context-menu {
  position: absolute;
  z-index: 10;
}

在继续介绍JavaScript之前,请记住,默认情况下,上下文菜单应处于隐藏状态。 我们将display设置为none ,并在需要显示时创建一个助手“活动”类。 而且,如果您想知道活动上下文菜单的位置,我们稍后也会解决。 这是我的其他CSS:

.context-menu {
  display: none;
  position: absolute;
  z-index: 10;
}

.context-menu--active {
  display: block;
}

免费学习PHP!

全面介绍PHP和MySQL,从而实现服务器端编程的飞跃。

原价$ 11.95 您的完全免费

现在,是时候准备我们的上下文菜单了。

部署上下文菜单-JavaScript

让我们从实际开始看如何注册contextmenu事件开始。 在我们JavaScript中,我们将打开一个自执行函数,并在整个文档中捕获该事件。 我们还将事件记录到控制台,以便我们可以窥探一些输出信息:

(function() {

  "use strict";

  document.addEventListener( "contextmenu", function(e) {
    console.log(e);
  });

})();

如果打开控制台并右键单击文档上的任意位置,您将看到确实记录了该事件。 那里有很多有趣的信息对我们有用,特别是位置坐标。 在防止默认行为之前,请考虑一下在文档的大多数部分上,我们实际上并不需要自定义行为。 我们只想在任务项上部署自定义上下文菜单。 考虑到这一点,让我们根据以下步骤进行一些构建:

  1. 我们需要遍历每个任务项,并向它们添加contextmenu事件侦听器。
  2. 对于每个任务项目上的每个事件侦听器,我们将阻止默认行为。
  3. 现在,让我们仅将事件和有问题的元素记录到控制台。

到目前为止,这里是:

(function() {

  "use strict";

  var taskItems = document.querySelectorAll(".task");

  for ( var i = 0, len = taskItems.length; i < len; i++ ) {
    var taskItem = taskItems[i];
    contextMenuListener(taskItem);
  }

  function contextMenuListener(el) {
    el.addEventListener( "contextmenu", function(e) {
      console.log(e, el);
    });
  }

})();

如果您检出控制台,您会注意到,每次我们单击任务项元素时,都会触发一个唯一事件。 现在让我们更进一步,并执行以下两个步骤:

  1. 右键单击任务项目时,将防止默认行为。
  2. 我们将通过向其添加帮助程序类来显示自定义上下文菜单。

不过,在进行此操作之前,让我们在菜单中添加一个context-menu ID,以使其易于获取,并在JavaScript中创建一个名为menuState的新变量。 我们将其默认设置为0 ,并假设0表示关闭,而1表示打开。 最后,让我们为活动类缓存一个变量,并将其称为active 。 这是我的三个附加变量:

var menu = document.querySelector("#context-menu");
var menuState = 0;
var active = "context-menu--active";

现在,让我们修改contextMenuListener函数,并添加一个toggleMenuOn来启动菜单:

function contextMenuListener(el) {
  el.addEventListener( "contextmenu", function(e) {
    e.preventDefault();
    toggleMenuOn();
  });
}

function toggleMenuOn() {
  if ( menuState !== 1 ) {
    menuState = 1;
    menu.classList.add(active);
  }
}

此时,右键单击任务项应部署菜单! 但是有些事情仍然无法正常工作。 首先,菜单不在我们右键单击某个项目时所期望的位置。 这需要用一些数学来修正。 其次,菜单出现后无法将其关闭。 如果我们观察到默认行为,就会发现以下几点:

  1. 单击菜单外部会使菜单返回到非活动状态。
  2. 按下退出键(或keycode 27)也会使菜单恢复为非活动状态。

让我们再进一步一点。 由于我们在应用程序的其他部分具有默认菜单,因此即使自定义菜单处于打开状态,用户也希望能够正常启动该菜单。 因此,在我们的上下文之外右键单击应关闭自定义菜单,并打开默认菜单。 这还没有发生。 让我们先解决所有这些打开/关闭问题,然后再定位。

重构我们的代码

现在显而易见,三个主要事件将负责触发动作:

  1. contextmenu –检查状态并部署上下文菜单。
  2. click –适用时隐藏菜单。
  3. keyup –负责键盘操作。 在本教程中,我们仅关注ESC键。

我们还将需要一些辅助函数,因此我们也需要在JavaScript中添加一个部分。 这是第一个重构:

(function() {

  "use strict";

  ///
  ///
  //
  // H E L P E R    F U N C T I O N S
  //
  ///
  ///

  /**
   * Some helper functions here.
   */

  ///
  ///
  //
  // C O R E    F U N C T I O N S
  //
  ///
  ///

  /**
   * Variables.
   */
  var taskItemClassName = 'task';
  var menu = document.querySelector("#context-menu");
  var menuState = 0;
  var activeClassName = "context-menu--active";

  /**
   * Initialise our application's code.
   */
  function init() {
    contextListener();
    clickListener();
    keyupListener();
  }

  /**
   * Listens for contextmenu events.
   */
  function contextListener() {

  }

  /**
   * Listens for click events.
   */
  function clickListener() {

  }

  /**
   * Listens for keyup events.
   */
  function keyupListener() {

  }

  /**
   * Turns the custom context menu on.
   */
  function toggleMenuOn() {
    if ( menuState !== 1 ) {
      menuState = 1;
      menu.classList.add(activeClassName);
    }
  }

  /**
   * Run the app.
   */
  init();

})();

这次,我们将不重复任务项目。 相反,我们将监听一个文档范围的contextmenu事件,并检查该事件是否发生在我们的任务项内。 这就是我们现在拥有变量taskItemClassName的原因。 为此,我们需要我们的第一个辅助函数clickInsideElement 。 它需要两个参数:

  1. 事件本身,我们可以从中检查事件的目标或源元素。
  2. 要比较的类名。 如果事件发生在具有类名或父项具有类名的元素内,则我们将希望返回该元素。

这是第一个辅助函数:

function clickInsideElement( e, className ) {
  var el = e.srcElement || e.target;

  if ( el.classList.contains(className) ) {
    return el;
  } else {
    while ( el = el.parentNode ) {
      if ( el.classList && el.classList.contains(className) ) {
        return el;
      }
    }
  }

  return false;
}

现在,我们可以通过将contextListener函数编辑为以下内容来返回之前的状态:

function contextListener() {
  document.addEventListener( "contextmenu", function(e) {
    if ( clickInsideElement( e, taskItemClassName ) ) {
      e.preventDefault();
      toggleMenuOn();
    }
  });
}

现在,借助我们的助手功能为我们做一些肮脏的工作,并在整个文档中监听一个contextmenu事件,当单击在上下文区域之外并且自定义菜单打开时,我们现在可以关闭菜单。 让我们添加一个toggleMenuOff函数,并编辑我们的contextListener函数:

function contextListener() {
  document.addEventListener( "contextmenu", function(e) {
    if ( clickInsideElement( e, taskItemClassName ) ) {
      e.preventDefault();
      toggleMenuOn();
    } else {
      toggleMenuOff();
    }
  });
}

function toggleMenuOff() {
  if ( menuState !== 0 ) {
    menuState = 0;
    menu.classList.remove(activeClassName);
  }
}

现在继续,右键单击任务项目。 现在,右键单击文档上的其他位置。 瞧! 自定义菜单会消失,默认菜单会照常显示。 现在,我们可以对常规click事件执行类似的操作:

function clickListener() {
  document.addEventListener( "click", function(e) {
    var button = e.which || e.button;
    if ( button === 1 ) {
      toggleMenuOff();
    }
  });
}

上面的测试与之前的测试有些不同,这是由于Firefox有点古怪。 释放右键单击按钮后,Firefox也会触发click事件,但它不是左键单击代码,因此我们必须确保该单击是左键单击。 这样,当我们右键单击某个项目时,菜单不会闪烁。 现在让我们再次为keyup事件做类似的事情:

function keyupListener() {
  window.onkeyup = function(e) {
    if ( e.keyCode === 27 ) {
      toggleMenuOff();
    }
  }
}

现在,我们成功地根据需要显示和隐藏菜单,并使用户体验尽可能自然。 让我们继续放置菜单,最后,我们将探讨一个用于处理菜单内事件的可能选项。

定位上下文菜单

由于我们当前设置了HTML和CSS,因此菜单仅显示在屏幕底部。 自然,我们希望它显示在我们单击的位置旁边。 让我们做到这一点。 首先,让我们利用另一个帮助器函数,该函数根据事件获取我们单击位置的确切坐标。 我将其称为getPosition ,它可以处理一些跨浏览器的怪癖以提高其准确性:

function getPosition(e) {
  var posx = 0;
  var posy = 0;

  if (!e) var e = window.event;

  if (e.pageX || e.pageY) {
    posx = e.pageX;
    posy = e.pageY;
  } else if (e.clientX || e.clientY) {
    posx = e.clientX + document.body.scrollLeft + 
                       document.documentElement.scrollLeft;
    posy = e.clientY + document.body.scrollTop + 
                       document.documentElement.scrollTop;
  }

  return {
    x: posx,
    y: posy
  }
}

放置菜单的第一步是准备三个变量。 让我们将这些添加到我们的变量块中:

var menuPosition;
var menuPositionX;
var menuPositionY;

现在,让我们创建一个名为positionMenu的函数,该函数接受一个参数-事件。 现在,让我们将排名结果记录到控制台:

function positionMenu(e) {
  menuPosition = getPosition(e);
  console.log(menuPosition);
}

现在,我们可以编辑contextListener函数以开始定位过程:

function contextListener() {
  document.addEventListener( "contextmenu", function(e) {
    if ( clickInsideElement( e, taskItemClassName ) ) {
      e.preventDefault();
      toggleMenuOn();
      positionMenu(e);
    } else {
      toggleMenuOff();
    }
  });
}

再次切换上下文菜单,然后检出控制台。 您会注意到该职位已记录下来,可供我们使用。 我们可以使用它通过JavaScript将topleft样式内联到菜单中。 这是更新的positionMenu函数:

function positionMenu(e) {
  menuPosition = getPosition(e);
  menuPositionX = menuPosition.x + "px";
  menuPositionY = menuPosition.y + "px";

  menu.style.left = menuPositionX;
  menu.style.top = menuPositionY;
}

立即单击并启动上下文菜单。 无论点击何处,它都会显示出来! 太棒了,但是我们需要考虑以下几点:

  1. 如果用户的屏幕有点狭窄,并且用户单击屏幕右侧的右边会发生什么? 上下文菜单将显示在屏幕外,并且主体将溢出。
  2. 如果在上下文菜单打开时用户调整窗口大小怎么办? 将会发生相同的溢出问题。 Dropbox通过隐藏x溢出来解决此问题。

让我们解决第一个问题。 我们将使用JavaScript确定菜单的宽度和高度,并检查以确保菜单的位置不会溢出。 如果是这样,我们将以与默认菜单类似的方式将其偏移几个像素。 这有点数学,需要一些思考,但是我们可以逐步进行。 首先,我们需要检查窗口的宽度和高度。 然后,我们需要找出菜单的宽度和高度。 最后,我们需要检查单击位置与窗口宽度加偏移量之差是否大于菜单宽度,并且高度是否相同。 如果更大,则我们手动设置位置,否则,我们将照常进行。 我们将从缓存两个宽度和高度的变量开始:

var menuWidth;
var menuHeight;

请记住,从前,我们的菜单默认情况下处于隐藏状态,因此我们尚无法计算宽度和高度。 无论如何,我们都是静态生成菜单,在像Dropbox这样的真实应用中,菜单的内容可能会根据上下文而变化。 然后,在部署菜单时计算宽度和高度是个好主意。 在positionMenu函数内部,我们可以像这样获得宽度和高度:

menuWidth = menu.offsetWidth;
menuHeight = menu.offsetHeight;

现在让我们为窗口的内部宽度和高度再准备两个变量:

var windowWidth;
var windowHeight;

同样,我们可以在部署菜单时计算它们,如下所示:

windowWidth = window.innerWidth;
windowHeight = window.innerHeight;

最后,假设我们只希望它距窗口边缘的距离仅4px。 我们可以像之前提到的那样比较我们的值,并正确放置菜单。 这是我想出的:

var clickCoords;
var clickCoordsX;
var clickCoordsY;

// updated positionMenu function
function positionMenu(e) {
  clickCoords = getPosition(e);
  clickCoordsX = clickCoords.x;
  clickCoordsY = clickCoords.y;

  menuWidth = menu.offsetWidth + 4;
  menuHeight = menu.offsetHeight + 4;

  windowWidth = window.innerWidth;
  windowHeight = window.innerHeight;

  if ( (windowWidth - clickCoordsX) < menuWidth ) {
    menu.style.left = windowWidth - menuWidth + "px";
  } else {
    menu.style.left = clickCoordsX + "px";
  }

  if ( (windowHeight - clickCoordsY) < menuHeight ) {
    menu.style.top = windowHeight - menuHeight + "px";
  } else {
    menu.style.top = clickCoordsY + "px";
  }
}

现在,即使我们在狭窄的窗口尺寸上触发它,菜单也应该表现出预期的效果! 如前所述,在调整窗口大小时,仍有溢出的可能性。 我之前曾提到过Dropbox如何解决这个问题,但是对于我们来说,让我们添加一个最终事件侦听器,以在调整窗口大小时关闭菜单。 在我们的init函数中,我们可以添加以下内容:

resizeListener();

函数将如下所示:

function resizeListener() {
  window.onresize = function(e) {
    toggleMenuOff();
  };
}

太棒了

将事件附加到上下文菜单项

如果您的应用程序比这更复杂,并且您需要动态生成上下文菜单内容,则必须考虑到这一点。 对于我们的应用程序,我们有一个具有相同选项的菜单。 因此,我们可以进行快速检查以查看单击了哪些项目,并触发某种操作。 再次以Dropbox为例,如果右键单击一个项目并按Delete键,将显示确认模式。 对于我们的应用程序,我们只需将当前任务项存储到变量中,然后将开始时设置的data-id属性记录到控制台。 我们还将记录相应的操作,因此让我们编辑上下文菜单标记以包括一些数据属性:

<nav id="context-menu" class="context-menu">
  <ul class="context-menu__items">
    <li class="context-menu__item">
      <a href="#" class="context-menu__link" data-action="View">
        <i class="fa fa-eye"></i> View Task
      </a>
    </li>
    <li class="context-menu__item">
      <a href="#" class="context-menu__link" data-action="Edit">
        <i class="fa fa-edit"></i> Edit Task
      </a>
    </li>
    <li class="context-menu__item">
      <a href="#" class="context-menu__link" data-action="Delete">
        <i class="fa fa-times"></i> Delete Task
      </a>
    </li>
  </ul>
</nav>

我们还有很多要更改和缓存的功能和对象,因此让我们逐步介绍它们。 首先,让我们缓存我们需要的所有对象:

var contextMenuClassName = "context-menu";
var contextMenuItemClassName = "context-menu__item";
var contextMenuLinkClassName = "context-menu__link";
var contextMenuActive = "context-menu--active";

var taskItemClassName = "task";
var taskItemInContext;

var clickCoords;
var clickCoordsX;
var clickCoordsY;

var menu = document.querySelector("#context-menu");
var menuItems = menu.querySelectorAll(".context-menu__item");
var menuState = 0;
var menuWidth;
var menuHeight;
var menuPosition;
var menuPositionX;
var menuPositionY;

var windowWidth;
var windowHeight;

要注意的一些新任务是taskItemInContext ,如果我们在任务项目上成功单击鼠标右键,则会分配该任务。 我们需要它来记录任务项目ID。 让我们还记下缓存的各种类名,如果我们更改标记,它们将使我们的功能的编辑变得容易。 现在让我们继续功能。

我们的初始化函数保持不变,但我们的第一个更改在于contextListener函数。 请记住,如果我们右键单击任务项目,我们想存储taskItemInContext变量。 如果我们有一个匹配项,我们的clickInsideElement帮助器实际上会返回一个元素,因此可以在此处进行设置:

function contextListener() {
  document.addEventListener( "contextmenu", function(e) {
    taskItemInContext = clickInsideElement( e, taskItemClassName );

    if ( taskItemInContext ) {
      e.preventDefault();
      toggleMenuOn();
      positionMenu(e);
    } else {
      taskItemInContext = null;
      toggleMenuOff();
    }
  });
}

如果右键单击不在任务项上,则将其重置为null 。 让我们继续clickListener函数。 就像我之前提到的,为了简化起见,我们只想将一些信息记录到控制台。 当前,当单击事件被注册时,我们进行了两次检查并关闭菜单。 但是,让我们对该功能进行一些简化,并检查该单击是否是上下文菜单中的项。 如果是这样,那么我们将执行操作,并在以下步骤后关闭菜单:

function clickListener() {
  document.addEventListener( "click", function(e) {
    var clickeElIsLink = clickInsideElement( e, contextMenuLinkClassName );

    if ( clickeElIsLink ) {
      e.preventDefault();
      menuItemListener( clickeElIsLink );
    } else {
      var button = e.which || e.button;
      if ( button === 1 ) {
        toggleMenuOff();
      }
    }
  });
}

您会注意到,单击上下文菜单项时会调用menuItemListener函数,因此我们将在一分钟内对其进行评估。 keyupListenerresizeListener函数保持不变。 对于toggleMenuOntoggleMenuOff函数,大多数情况相同,唯一的区别是更改了活动类的变量名称,从而提供了更好的代码可读性:

function toggleMenuOn() {
  if ( menuState !== 1 ) {
    menuState = 1;
    menu.classList.add( contextMenuActive );
  }
}

function toggleMenuOff() {
  if ( menuState !== 0 ) {
    menuState = 0;
    menu.classList.remove( contextMenuActive );
  }
}

positionMenu函数也保持不变,最后,这是新的menuItemListener函数,它接受一个参数:

function menuItemListener( link ) {
  console.log( "Task ID - " + 
                taskItemInContext.getAttribute("data-id") + 
                ", Task action - " + link.getAttribute("data-action"));
  toggleMenuOff();
}

这将我们带到功能性的尽头……phe!

一些注意事项和注意事项

在总结之前,让我们考虑以下几点:

  1. 在整篇文章中,我都提到“右键单击”是启动上下文菜单的事件。 不是每个人都是对的,也不是每个人都有相同的鼠标设置。 无论如何, contextmenu事件将根据用户的鼠标设置进行操作。
  2. 需要注意的另一个重要点是,我们仅考虑了成熟的桌面应用程序。 用户可能正在通过键盘输入或移动技术等来访问您的应用或网站。 您需要确保,如果您决定更改默认行为,则必须在所有类型的可访问性上都获得一致的用户友好体验。

考虑到这些因素,您应该在使该组件更进一步的道路上步入正轨。

大问题

我将最后的考虑因素保留在其单独的段落中,因为最重要的是,在本教程涵盖的所有内容之上,还有一个大问题:您真的需要自定义上下文菜单吗? 自定义行为是很酷的事情,今天我们在这里做了一些非常有趣的工作,但是您确实必须确保您的应用程序绝对会从诸如此类的事情中受益。 用户访问网络时会期望某些东西。 最好的示例是,如果您的应用程序有照片,并且在该照片上方是您的自定义上下文菜单。 您绝对应该记住,用户右键单击它会希望能够下载它,复制链接,在新选项卡中打开图像以及其他标准选项。

浏览器兼容性

本教程利用了一些现代CSS和JavaScript,即flex用于CSS布局,而classList用于JavaScript中的类切换。 如果您需要与旧版浏览器向后兼容,建议您进行相应的编辑。 本教程已在以下浏览器中进行了测试:

  • 镀Chrome39
  • Safari 7
  • 火狐33

在这里还值得一提的是,HTML5中有一个上下文菜单/工具栏规范 ,但是不幸的浏览器支持几乎没有,因此不值得深入研究。 在可预见的将来,定制解决方案是唯一的真实选择。

总结和演示

如果您仔细考虑了所有内容,并且100%确定您的应用程序可以使用这种类型的功能,请继续使用。 今天,本教程已经讨论了很多内容,您现在应该对上下文菜单是什么,如何在应用程序中实现上下文菜单以及一些重要的考虑因素有深刻的了解。希望您喜欢阅读,我希望期待您的评论!

供您参考,本教程的代码库可在GitHub上找到 ,并将随着时间的推移进行维护和更新。 有关本教程中使用的代码的迭代以及完整的演示工作,请查看下面的CodePen演示。

请参阅CodePenSitePoint的Pen MYLoWY@SitePoint )。

翻译自: https://www.sitepoint.com/building-custom-right-click-context-menu-javascript/

javascript上下文

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值