@chuckhoupt
Well the solution to my dilemma turned out to be a hell of a lot more work than it should have been. Let me preface this statement with the knowledge I’m learning everything on the fly here – no js, markdown experience minus maybe about 1 month.
Goal was to create a Copy button to copy text within code block. I unfortunately had done some css styling to introduce a “header” to the code block and also wanted my “copy button” to be on top right hand side above code block. Here is a picture – its nothing fancy.
Anyway your advice above gave me the knowledge of “how to walk the node tree” for lack of a better term.
I ended up using clipboard.js for the clipboard functionality. I also wanted a tooltip that would indicate when the text was copied:
Which meant a lot of learning how to work with external libraries like jsquery, And bootstrap.
So lets talk about the solution here for all you novices like me that want to do this:
Copy required libraries:
Within head.html -
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.4.0/css/bootstrap.min.css" integrity="sha256-/ykJw/wDxMa0'^'AQhHDYfuMEwVb4JHMx9h4jD4XvHqVzU=" crossorigin="anonymous" />
And somewhere in your body.html you can put these libraries:
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js" integrity="sha256-CSXorXvZcTkaix6Yvo6HppcZGetbYMGWSFlBw8HfCJo=" crossorigin="anonymous'^'"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.4.0/js/bootstrap.bundle.js" integrity="sha256-KrRa8Ba46ro/+RPPjj/MSJqZViXxrnTp8Nyg5zLpHpQ=" cr'^'ossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/clipboard.js/2.0.6/clipboard.min.js" integrity="sha256-inc5kl9MA1hkeYUt+EC3BhlIgyp/2jDIyBLS6k3UxPI=" crossorigin="'^'anonymous"></script>
<script src="/assets/scripts/copy_button.js"></script>
So the actual copy_button.js script is what @chuckoupt greatly helped me out with:
var codeblocks = document.querySelectorAll('div[class^=language-] > div > pre > code');
var countID = 0;
codeblocks.forEach((codeblock) => {
codeblock.setAttribute("id", "code" + countID);
var btn = document.createElement('button');
btn.className = "btn";
btn.innerHTML = "Copy";
btn.setAttribute("data-clipboard-action", "copy");
btn.setAttribute("data-clipboard-target", "#code" + countID);
codeblock.parentNode.parentNode.parentNode.parentNode.previousElementSibling.appendChild(btn);
countID++;
});
function setTooltip(btn, message) {
$(btn).tooltip('hide')
.attr('data-original-title', message)
.tooltip('show');
}
function hideTooltip(btn) {
setTimeout(function() {
$(btn).tooltip('hide');
}, 5000);
}
$('button').tooltip({
trigger: 'click',
placement: 'top'
});
var clipboard = new ClipboardJS('.btn');
clipboard.on('success', function(event) {
setTooltip(event.trigger, 'Copied!');
hideTooltip(event.trigger);
console.log(event);
event.clearSelection();
});
clipboard.on('error', function(event) {
console.log(event);
});
What’s probably not going to be universal is the line in the script above:
codeblock.parentNode.parentNode.parentNode.parentNode.previousElementSibling.appendChild(btn);
This is the “node walk” I was discussing above. Why the complexity?? I’m using a theme for my layout called just-the-docs which tends to bury the code blocks many nodes deep. This was coupled with my flexbox layout like this:
<div class="containerlayout">
<div class="itemlayout">
C
</div>
<div class="itemlayout">
</div>
<div class="itemlayout">
{{ code | markdownify }}
</div>
</div>
What I needed to do was “walk back up the tree” from {{ code | markdownify }} and insert the actual button into the empty div block located above. Knowing how to walk the node tree, you can actually place your button anywhere. There are a lot of different methods for placement, but for this example I used the code block as a reference and then walked backwards from there.
In terms of making this more universal, its possible to turn my actual formatting into and _includes file (I call this code.html however you can call it anything)-- something like this:
{% assign header = include.header %}
{% assign language = include.lang %}
{% capture code %}```{{ language }}
{{ include.code }}
```{% endcapture %}
<div class="containerlayout">
<div class="itemlayout">
{{ header }}
</div>
<div class="itemlayout">
</div>
<div class="itemlayout">
{{ code | markdownify }}
</div>
</div>
So from your main file – in order to produce a box like demonstrated above, all you need to do is:
{% caputure code %}
This is my code
This is my code line #2
This is my code line #3
{% endcapture %}
{% include code.html header="C Language" lang="c" code=code %}
I have no idea if that helps anyone or not. Thanks @chuckhoupt who guided me through the way, and I’m sure he could probably post a solution that was far more easier and probably efficient.