Jonnie Grieve Digital Media: Blog

Home
by on 6th April, 2023 - 1:22pm (0)

Blog: Using the Clipboard API to copy git clone commands (More Posts)

In this blog, I’m going to run through a solution for what sounds like a simple brief but it was by no means an easy solution to implement. I ran into a number of stumbling blocks but what I’m about to go through is one solution. And not necessarily the optimal one.

The brief? Using the Navigator Clipboard API to copy supplied git clone commands so anyone using the web page can paste them into their Command Line Interfaces (CLI).

On this page, I have a list of repositories that are output using an external JSON file. And in that file, there are many data fields. For things like image paths, URLs, and project status tags.

  • “repo_name”
  • “repo_img”
  • “repo_alt”
  • “repo_description”
  • “repo_url”
  • “repo_type”
  • “repo_status”
  • “repo_clone”
  • “repo_privacy”

And in order to retrieve the data we use the jQuery getJSON method, which has a string argument pointing to the file.

Like this…

jQuery.getJSON('files/data/repositories.json', function(photoData) {

let itemAll = photoData.length;
    for (let i=0; i < itemAll; i++) {

        <div class="html_container">

            <a href="#">  ${ photoData[i].repo_url } </a>

        </div>
    }
}

My example requires an image, some text for the title of the project, and at least 3 span elements that contain information about the given repository. We need some way to be able to copy the git clone command for the repository to the clipboard.

To do this, I added a condition statement to the JSON object, that checks for repositories that are public on GitHub. If the repository is public, I will provide the command clone the repository to the browser. If it is not public, there’s no clone command.

if ( photoData[i].repo_privacy === "public") {
    // public repositoriespublic repositories

} else {
    // return alternative data based on non public repositories
}

Any information it does find will be put into the container element with the class of list-js.

<main>

    <h2>List (index.php)</h2>
    <p>A Dynamic list of my public GitHub Repositories.</p>

    <article id="list_id" class="list-js">

    </article>

</main>

Now, to use clipboard functionality you need something called the navigator.clipboard API. You also need to make sure you’re able to select the appropriate elements or sometimes a group of elements and log them to your browser console to check the copying has worked. As you can imagine, I ran into a number of problems along the way.

Last week I blogged about ChatGPT and how it can use useful, for interpreting problem briefs and applying them to your projects. I used it to run through a number of scenarios around one theme. To paraphrase, I asked it to return me some code like this “select from a group of elements and then copy the text content of a clicked elements“.

And it did run through some scenarios for me each of which I tried to adapt for my own needs. But for each of these scenarios, I was getting no text added to the DevTools console and no text on the clipboard for me to test on. So how could I know that ChatGPT could be any good for me?

Just to make sure the API would work, I reverse-engineered the solution directly from Chat GPT, recreating it in my text editor.  So I set about going to basics. I was provided with a container div element with 3 child div elements.

<!doctype html >
<html>

<head>
    <title> Using the clipboard to copy text content </title>
    <link rel="stylesheet" href="style.css" />
</head>

<body>

    <div id="group">
        <div class="item">Item 1</div>
        <div class="item">Item 2</div>
        <div class="item">Item 3</div>
    </div>

    <script type = "text/javascript" src="app.js"></script>
</body>

</html>

In JavaScript we select the elements and loop through the 3 items waiting for a click event to be activated on the child elements.

// Get the group of elements
const group = document.getElementById('group');

// Get all the items in the group
const items = group.querySelectorAll('.item');

// Add a click event listener to each item
items.forEach(item => {
    item.addEventListener('click', () => {
        // Get the text content of the clicked item
        const text = item.textContent;

       // Copy the text to the clipboard
       navigator.clipboard.writeText(text)
       .then(() => {
           console.log(`Copied "${text}" to clipboard`);
        })
        .catch(err => {
            console.error('Failed to copy text: ', err);
        });
    });
});

Selects from a div element that has the class of .group.

const group = document.getElementById('group');

Each list item that has simple text content has a class of .item.

const items = group.querySelectorAll('.item');

Nice and simple.

Next, the code uses a forEach iteration loop and the clipboard API to copy a formatted message to clipboard. Using the item parameter variable means the browser is waiting for a mouse click on any of the elements that have the class of action. And if it does, it displays the contents of the text to the console which proves that the text has also been put on the clipboard.

console.log(`Copied "${text}" to clipboard`);

Copied "Item 3" to the clipboard

So you can go to the browser click on any of the items and then right-click to paste the contents. It proves the validity of the code being given to me by the AI.

So now I know it works. I had thought that it might be missing a function call or something like that but I know now that the code the AI generates is sound. The only other thing I could think could possibly be why this is not working is a problem selecting the elements with JavaScript DOM methods. The above example works because it it’s a simple case of one containing element with 3 child elements. There’s no requirement for any DOM traversal and nothing to complicate the process.

This, however, is what the document tree looks like for my example.

#list_id
    .repo_item

       .gitclone-textbox

#list_id
    .repo_item

        .privacy .gitclone_value

I have to find a way of retrieving the text down 2 layers in the document tree.

Let’s look at some console logs to see what they are retrieving right now.

const group = document.querySelector('#list_id');console.log( group );

Which returns the list of all div elements in the class of repo in their containing element. At the time of writing this post there were 20 child elements returned.

// let repo_item = group.querySelectorAll('.repo_item');
// console.log(repo_item);

This kept returning a NodeList with a length property of 0 which means there’s no values in it. So although JavaScript was correctly selecting the element it was both returning the wrong type and had no access to the data I need.

I want to use one button to get the text content of another. But instead, I’m getting nothing. I’m going to have to change my methods again.

Here’s how I eventually got it to work.

In the getJSON method, I have 4 span classes. To get the four, I separated the privacy and gitclone_value classes. I modified the markup in jquery .getJSON methods to include the clone command for all public repositories so I could select text directly in the method rather than having to traverse it.

This 4th element now spans the whole width of each item on its own line.

<span class="type"> (${ photoData[i].repo_type }) </span> |
<span class="status"> (${ photoData[i].repo_status }) </span> |
<span class="privacy"> (${ photoData[i].repo_privacy }) </span>
<span class="gitclone_value" data-clone="${ photoData[i].repo_clone }" 
    title="click to copy clone command">${ photoData[i].repo_clone }
</span>

I add a function call to the onclick() attribute of .gitclone_value.

onclick="copyTextContent(event)"

And then in JavaScript I will write the copyTextContent() function.

The function takes the event object as an argument.

I added in the console log that shows us the element that was clicked.

I then simply capture the Text content of the clicked element in the node and checked that this has correctly logged to the console.

If it has worked we should be seeing a log in the console like this.

git clone https://github.com/jg-digital-media/jg-digital-media.github.io

The full function is below.

function copyTextContent(event) {
    const selectedElement = event.target;
    console.log(selectedElement);
    const textToCopy = selectedElement.textContent;
    navigator.clipboard.writeText(textToCopy);
    console.log(textToCopy);
}

Now I have a functional repository list that provides the means to grab the appropriate Hit clone command that you can out into your command line or Terminal for quick cloning of my Git Repositories.

 

This post has been assigned to the following categories

    Leave a Reply

    Your email address will not be published. Required fields are marked *