为什么复制在上下文菜单中停止工作?

时间:2017-06-01 09:10:01

标签: javascript jquery-mobile knockout.js clipboard knockout-3.0

我遇到了调试问题,我怀疑这是某种浏览器安全协议。

这是一些代码的最小示例,这些代码是通过让每个部分都有一个可点击的图标来复制文本而重构的。引入一个上下文菜单,该菜单应该委托给以前负责复制的视图模型。

我期待发生的事情,是蓝色,绿色和红色在右键单击并选择复制时将0,1,2输出到剪贴板。

但是,自从我介绍了上下文菜单后,事情就停止了。

我知道浏览器执行副本对用户交互有限制,但是肯定左键单击菜单选项是用户交互吗?

或者我犯了一个我看不见过去的愚蠢错误?

ClickDirect正常工作,因为它将函数直接绑定到单击处理程序。

但是没有其他div复制。

var contextMenuVM = new function() {
  var self = this;
  var piece = {};
  var args = [];

  self.show = function(data, event) {
    console.log('showargs:', arguments);
    console.log('showthis:', this);
    event.stopPropagation(true);
    piece = this;
    args = arguments;
    event.stopPropagation();
    var posx = event.clientX + window.pageXOffset; //Left Position of Mouse Pointer
    var posy = event.clientY + window.pageYOffset; //Top Position of Mouse Pointer
    $('#contextMenu').popup('open', {
      x: posx,
      y: posy,
      positionTo: 'origin'
    });
    return false;
  };

  self.clickHandler = function(fn) {
    return function(vm, event) {
      event.stopPropagation();
      event.preventDefault();
      console.log('clickargs:', arguments);
      console.log('clickthis:', this);
      fn.apply(piece, args);
      //$('#contextMenu').popup('close');
      return false;
    };
  };
}();

copyToClipboard = function(pstrText) {

  // create hidden text element, if it doesn't already exist
  var targetId = "_hiddenCopyText_";
  var origSelectionStart, origSelectionEnd;

  // must use a temporary form element for the selection and copy
  var target = document.getElementById(targetId);
  if (!target) {
    target = document.createElement("textarea");
    target.id = targetId;
    document.body.appendChild(target);
  }
  target.textContent = pstrText;
  // select the content
  var currentFocus = document.activeElement;
  target.focus();
  target.setSelectionRange(0, target.value.length);

  // copy the selection
  var succeed;
  try {
    succeed = document.execCommand("copy");
    console.log('succeed:', succeed);
  } catch (e) {
    succeed = false;
    console.log('exception', e);
  }
  // restore original focus
  if (currentFocus && typeof currentFocus.focus === "function") {
    //currentFocus.focus();
  }

  // clear temporary content
  // target.textContent = "";

  return succeed;
};

toolbox = new function() {
  var self = this;
  self.copy = function() {
    console.log('toolboxargs: ', arguments);
    console.log('toolboxthis:', this);
    copyToClipboard(this.number);
  };
}();

var pieceVM = function(number) {
  var self = this;
  self.number = number;
};

var arr = ['bluemenu', 'greenmenu', 'redmenu', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l'];

var count = 0;
$(function() {
  $(".piece").each(function() {
    ko.applyBindings(new pieceVM(arr[count++]), this);
  });

  ko.applyBindings(contextMenuVM, document.getElementById('contextMenu'));

});
<!DOCTYPE html>
<html lang="en">

<head>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
  <link rel="stylesheet" type="text/css" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" />
  <link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
  <link rel="stylesheet" href="https://code.jquery.com/mobile/1.3.2/jquery.mobile-1.3.2.min.css" />
  <script src="https://code.jquery.com/mobile/1.3.2/jquery.mobile-1.3.2.min.js"></script>


</head>

<body>
  <div data-role="page">
    <div id="toolbox"></div>
    <div data-role="content">
      <div style="margin-top:100px">
        demo
        <div class="row">
          <div class="piece col-xs-4 bg-info" data-bind="event:{contextmenu: contextMenuVM.show}">
            blue contextmenu - not working (succeed: true)
          </div>
          <div class="piece col-xs-4 bg-success" data-bind="event:{contextmenu: contextMenuVM.show}">
            green contextmenu - not working (succeed: true)
          </div>
          <div class="piece col-xs-4 bg-danger" data-bind="event:{contextmenu: contextMenuVM.show}">
            red contextmenu - not working (succeed: true)
          </div>
        </div>
        <div class="row">
          <textarea name="_hiddenCopyText_" id="_hiddenCopyText_" cols="30" rows="1"></textarea>
        </div>
        <div class="row">
          <div class="col-xs-12" style="padding-top:5px;">
            <div class="row">
              <div class="col-xs-12">testing (scroll down)</div>
            </div>
            <div class="row">
              <div class="expected col-xs-3 bg-success">(expected: working)</div>
              <div class="result col-xs-3 bg-danger">(result: broken)</div>
              <div class="piece col-xs-6 bg-warning" data-bind="event: {contextmenu: contextMenuVM.show}">
                context menu (same as demo)
              </div>
            </div>
            <div class="row">
              <div class="expected col-xs-3 bg-success">(expected: working)</div>
              <div class="result col-xs-3 bg-danger">(result: broken)</div>
              <div class="piece col-xs-6 bg-warning" data-bind="click: contextMenuVM.show">
                left click menu
              </div>
            </div>
            <div class="row">
              <div class="expected col-xs-3 bg-success">(expected: working)</div>
              <div class="result col-xs-3 bg-success">(result: working)</div>
              <div class="piece col-xs-6 bg-primary" data-bind="click: function(){copyToClipboard('click direct')}">
                click direct copy
              </div>
            </div>
            <div class="row">
              <div class="expected col-xs-3 bg-danger">(expected: broken)</div>
              <div class="result col-xs-3 bg-danger">(result: broken)</div>
              <div class="piece col-xs-6 bg-warning" data-bind="event: {contextmenu: copyToClipboard('context direct')}">
                context direct copy
              </div>
            </div>
            <div class="row">
              <div class="expected col-xs-3 bg-success">(expected: working)</div>
              <div class="result col-xs-3 bg-success">(result: working)</div>
              <div class="piece col-xs-6 bg-primary" data-bind="click: contextMenuVM.clickHandler(function(){copyToClipboard('click indirect')})">
                click indirect
              </div>
            </div>
            <div class="row">
              <div class="expected col-xs-3 bg-danger">(expected: broken)</div>
              <div class="result col-xs-3 bg-danger">(result: broken)</div>
              <div class="piece col-xs-6 bg-warning" data-bind="event: {contextmenu: contextMenuVM.clickHandler(function(){copyToClipboard('context indirect')})}">
                context indirect
              </div>
            </div>
          </div>
        </div>
      </div>

      <div id="contextMenu" class="contextMenu ui-content" data-role="popup" data-theme="c" data-dismissible="true">
        <div title="Copy" data-bind="click: clickHandler(toolbox.copy)"><i style="cursor: pointer;" class="fa fa-copy"></i><span class="contextMenuItemText">Copy</span></div>
        <div title="Copy"><a data-bind="click: clickHandler(toolbox.copy)"><i style="cursor: pointer;" class="fa fa-copy"></i><span class="contextMenuItemText">Copy</span></a></div>
        <div title="Copy" data-bind="event: {click: clickHandler(toolbox.copy)}"><i style="cursor: pointer;" class="fa fa-copy"></i><span class="contextMenuItemText">Copy</span></div>
        <div title="Copy"><a data-bind="event: {click: clickHandler(toolbox.copy)}"><i style="cursor: pointer;" class="fa fa-copy"></i><span class="contextMenuItemText">Copy</span></a></div>
        <a data-bind="event: {click: clickHandler(toolbox.copy)}" class="ui-btn">Copy</a>
        <div title="Copy" data-bind="click: clickHandler(function(){copyToClipboard('click indirect')})">click indirect</div>
      </div>
    </div>
  </div>

</body>

</html>

1 个答案:

答案 0 :(得分:1)

它停止工作,因为JQuery Mobile Popups阻止焦点转到弹出窗口之外的元素。

您正在使用的代码(已在一些地方普及,包括堆栈溢出复制)依赖于隐藏元素获得关注。

在你的情况下,你可能有点不幸,因为你有一个硬编码的效用函数,假设焦点可能会丢失。

如果您可以编辑该实用程序,我建议您在每个弹出窗口中使用多个这些字段,并使用最近的字段,或者在调用复制功能时在弹出窗口中提供元素的ID。

幸运的是,你的问题不是要求解决方案,而是为什么会出现问题。否则我无法帮助你:P。花了6个小时......很难成为我们。