Twitter Email
subspace (/ˈsʌbspɛɪs/)
A Jekyll playground site

A tag to insert a code selector widget

Last modified: , 387 Words
14:36 on Monday, 11. October 2021 | by Admin in Jekyll

This is very simple. The tag inserts a small <div> with clickable text. When clicked, the contents of the following highlight block will be selected so it can be easily copied. Note that the codeselect tag must stand immediately before the highlight tag (literally, the line before, otherwise it may not work). It also works with the markdown method of code highlighting (three backticks) and is smart enough to NOT select the line numbers in case they are present. {:dc

Yes, I’m aware that there are different and maybe easier solutions for this problems, some are CSS and JavaScript - only, but this solution is slightly smarter, because it’s optional.

Here is the plugin code.
Code: (click to select all)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
module Jekyll
  class SelTag < Liquid::Tag

    def initialize(tag_name, text, tokens)
      super
      @text = text
    end

    # render the output. Make sure, we have a valid cssclass
    def render(context)
      output = "<div class=\"codeselect\">Code: (click to select all)</div>"
      return output
    end
  end
end
Liquid::Template.register_tag('codeselect', Jekyll::SelTag)

Javascript

This is the necessary JavaScript. It consists of 2 parts. First, a generic jQuery plugin to select the text of a given node. Just put this somewhere into your site’s footer scripts. It must load after jQuery.

Code: (click to select all)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// jQuery plugin to select text of a node
jQuery.fn.selectText = function(){
    this.find('input').each(function() {
        if($(this).prev().length == 0 || !$(this).prev().hasClass('p_copy')) { 
            $('<p class="p_copy" style="position: absolute; z-index: -1;"></p>').insertBefore($(this));
        }
        $(this).prev().html($(this).val());
    });
    var doc = document;
    var element = this[0];

    console.log(this, element);
    if (doc.body.createTextRange) {
        var range = document.body.createTextRange();
        range.moveToElementText(element);
        range.select();
    } else if (window.getSelection) {
        var selection = window.getSelection();
        var range = document.createRange();
        if (typeof(element) == 'object') {
          range.selectNodeContents(element);
        } else {
          console.log(' not an object ' + typeof(element));
        }
        selection.removeAllRanges();
        selection.addRange(range);
    }
};

The following small JavaScript fragment should go to your $(document).ready() function. It handles the clicks and does the actual work.

Code: (click to select all)
1
2
3
4
5
6
7
8
9
10
    $('div.codeselect').on('click', function(event) {
      // this is for a highlight witOUT linenos or the markdown method
      if ( $(this).next().contents().find('code').length ) {
        $(this).next().contents().find('code').selectText();
      }
      // and this is for linenos
      if ( $(this).next().contents().find('td.code').length ) {
        $(this).next().contents().find('td.code').selectText();
      }
    });