Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

copying & selecting a span programmatically in Javascript (with clipboard.js)

Context

The real free software (GPLv3) application (motivating this question) that I am coding is the MELT monitor on github (FWIW, I am at commit 56a83d358d3966eddb55... on February 11th, 2016). It is on Linux/x86_64/Debian, Firefox/44, JQuery-2.2.0, JqueryUI 1.11.4, clipboard.js 1.5.8....

A more open (but without code) related question was downvoted on Programmers. So I made a JsFiddle example for this very question, but that JsFiddle example is probably very wrong.

I have a (dynamically generated) HTML document with some <span>-s of class='momitemref_cl' like <span class='momitemref_cl'>this</span> etc... (the inside content of such spans is always a single word like this, actually some variable name or identifier in some DSL of mine). My goal is to have a dynamically generated menu for each such spans to enable some operations on them, in particular: hilight every occurrence of that word, and copy & select the word to the browser's and desktop's clipboard.

There are many questions here about copying to the clipboard in Javascript, such as this one. Several of them mention Zeno Rocha's clipboard.js which I am trying to use.

Code with explanation

To explain some of the JsFiddle: I am creating the JQueryUI menu with

// make a menu for the given span
function make_menu(spa) {
  console.log("make_menu spa=", spa);
  var name = $(spa).text();
  mom_menuitemcount++;
  var menuid = "menuid_" + mom_menuitemcount;
  console.log("make_menu name=", name, " menuid=", menuid);
  $maindiv.after("<ul class='mommenu_cl' id='" + menuid + "'>"
               + "<li class='ui-state-disabled'>* <i>" + name + "</i> *</li>"
    // the text inside the following <li> matters
    + "<li>Hilight</li>" + "<li>Copy</li>" + "</ul>");
  var mymenu = $('#' + menuid);
  mymenu.menu({
    select: function(ev, ui) {
      var uitem = ui.item;
      var utext = uitem.text();
      console.log("mymenu-select ev=", ev, " ui=", ui, " name=", name,
        " uitem=", uitem, " utext=", utext);
      switch (utext) {
        case "Hilight":
          $maindiv.find(".momitemref_cl").each(function(ix, el) {
            var etext = $(el).text();
            console.log("hilighting el=", el, " etext=", etext);
            if (etext == name) {
              $(el).toggleClass("momhilight_cl");
            }
          });
          break;
        case "Copy":
          //// what should go here?
          break;
      };
      setTimeout(200, destroy_menu);
    },
    position: {
      my: "left top",
      at: "bottom left",
      of: spa
    }
  });
  mom_menuitem = mymenu;
  console.log("make_menu spa=", spa, " mymenu=", mymenu);
  return mymenu;
}

In the above code, I don't know what to put in //// what should go here?; perhaps it might be something similar to:

uitem.select();
document.execCommand('Copy');

but AFAIU this don't work as expected, and I feel that clipboard operations need the user event (e.g. a mouse click or down) trigerring them.

The clipboard is initialized with

$clipboardh = new Clipboard(".momitemref_cl", {
  text: function (trig) {
    console.log("clipboard text trig=", trig);
    /// some code is missing here probably
  };
});

I guess (but I am not sure) that the /// some code is missing here probably should return the text value of the relevant span.

Questions

So I don't know how to copy the span's content to the clipboard (//// what should go here? ....) and how to put into the clipboard its text (/// some code is missing here probably....)

The full MVCE code is this JsFiddle

In other words, how to use clipboard.js from a JQueryUI menu item to copy a span's content to the browser's clipboard ??


Perhaps the entire approach is wrong, and perhaps I should use contextmenu (but then, how to customize it only for span-s of class='momitemref_cl' ?)

To make things even more complex, I actually have in MELT monitor (but not in this JsFiddle...) two CSS classes mom_itemref_cl & mom_itemval_cl that I want to behave likewise.


PS. I messed the JsFiddle when copying code here. I updated it, https://jsfiddle.net/bstarynk/g2notLd7/132/

NB. I could be satisfied with an answer working only on recent Firefox & Chrome.

like image 977
Basile Starynkevitch Avatar asked Mar 14 '23 07:03

Basile Starynkevitch


1 Answers

Clipboard.js needs to be attached to the element that triggers the copy event. In your case, this is the <li>Copy</li> element inside the mommenu_cl menu, which you create dynamically when a momitemref_cl is right clicked. You can attach clipboard.js to the Copy li dynamically when you create the menu itself. You will also have to destroy the clipboard when you destroy the menu.

First, let's fix the typo in your updated fiddle, on line 87:

curmenu[0].stle.left = Math.round(ev.pageY) + 5;

Then, remove the clipboard from your DOM's ready handler, we'll move this to the make_menu function:

$(document).ready(function() {
  $maindiv = $('#maindiv_id');
  // no clipboard initialization here
  console.log("our document ready function");
  ...
});

Next, at the top of your script, were you first declare $clipboardh, initialize it to false. Later, you will be checking to make sure you only attach the clipboard in case it is not currently attached.

/// javascript code
var $clipboardh = false;

In your make_menu function, give your <li>Copy</li> element a class="copy" attribute so you can easily attach clipboard.js to it, and a data-clipboard-text attribute set to the value you want clipboard.js to copy when the element is clicked.

Because the name variable contains the text value you want to copy, your menu will look something like:

$maindiv.after("<ul class='mommenu_cl' id='" + menuid + "'>" +
        "<li class='copy' data-clipboard-text='" + name + "'>Copy</li>" + 
    "</ul>");

Next, just after this code in your make_menu function, attach clipboard.js, only if it was not already attached:

if ($clipboardh === false){
  console.log("creating the clipboard");
  $clipboardh = new Clipboard('.copy');

  // some optional event handlers for debugging
  $clipboardh.on('success', function(e) {
      console.info('Action:', e.action);
      console.info('Text:', e.text);
      console.info('Trigger:', e.trigger);

      e.clearSelection();
  });

  $clipboardh.on('error', function(e) {
      console.error('Action:', e.action);
      console.error('Trigger:', e.trigger);
  });
}

Finally, you will want to make sure you destroy the clipboard when you destroy the menu, by adding this to the bottom of your destroy_menu function:

$clipboardh.destroy();
$clipboardh = false;

You can find a working fiddle here.

like image 111
Stiliyan Avatar answered Apr 26 '23 20:04

Stiliyan