IMPLEMENTED: Make the element IDs copy to clipboard on click

I would very much like to see this added. Or at least a small copy icon. I understand that clicking is already tied to opening the global class menu.

2 Likes

For those interested in a DIY method while we wait, I threw something together today. If you find any issues, let me know.

Add this to your functions.php file:

add_action( 'wp_enqueue_scripts', function() {
	if ( bricks_is_builder_main() ) {
		wp_enqueue_script( 'stu-element-copy-id', get_stylesheet_directory_uri() . '/stu-element-copy-id.js');
	}
} );

And put this in a file called stu-element-copy-id.js inside your child theme:

function stuElementCopyId() {
	const copyIcon = '<svg version="1.1" viewBox="0 0 30 30" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" class="bricks-svg"><path d="M9.5,5h11c0.276,0 0.5,-0.224 0.5,-0.5v-1c0,-1.378 -1.122,-2.5 -2.5,-2.5h-1.774c-0.353,-0.609 -1.007,-1 -1.726,-1c-0.718,0 -1.373,0.39 -1.726,1h-1.774c-1.378,0 -2.5,1.122 -2.5,2.5v1c0,0.276 0.224,0.5 0.5,0.5Zm14.766,-2h-1.766c-0.276,0 -0.5,0.224 -0.5,0.5v1c0,0.828 -0.672,1.5 -1.5,1.5h-11c-0.826,0 -1.5,-0.672 -1.5,-1.5v-1c0,-0.276 -0.224,-0.5 -0.5,-0.5h-1.765c-0.956,0 -1.735,0.781 -1.735,1.739v23.523c0,0.957 0.779,1.738 1.735,1.738h18.529c0.957,0 1.736,-0.781 1.736,-1.739v-23.522c0,-0.958 -0.778,-1.739 -1.734,-1.739Z" fill="currentColor"></path></svg>';

	const span = document.createElement('span');
	span.setAttribute('class', 'bricks-svg-wrapper stu-copy');
	span.setAttribute('data-balloon', 'Copy (Element ID)');
	span.setAttribute('data-balloon-pos', 'top-right');
	span.innerHTML = copyIcon;

	const style = document.createElement('style');
	style.innerHTML = `
		#bricks-panel-element-classes .active-class .actions .stu-copy {
			visibility: hidden;
		}
		#bricks-panel-element-classes .active-class:hover .actions .stu-copy {
			visibility: visible;
		}
	`;
	document.head.appendChild(style);

	const targetNode = document.getElementById('bricks-panel'),
		  config = {childList: true, subtree: true},
		  callback = (mutationList, observer) => {
			  for (const mutation of mutationList) {

				      // If changing element being edited
				  if (mutation.target.id == 'bricks-panel-element' ||
					  // If going from settings panel or element picker to editing element
					  mutation.addedNodes.length !== 0 && mutation.addedNodes[0].id === 'bricks-panel-element') {

					  const activeClass = document.querySelector('#bricks-panel-element-classes .active-class'),
							placeholder = activeClass.querySelector('input').getAttribute('placeholder'),
							dataId = document.getElementById('bricks-builder-iframe').contentWindow.document.querySelector(placeholder).getAttribute('data-id'),
							actionsContainer = activeClass.querySelector('div.actions');

					  actionsContainer.prepend(span);

					  const copy = actionsContainer.querySelector('span.stu-copy');

					  copy.addEventListener('click', function(e) {
						  e.stopPropagation();

						  // Comment this line and uncomment the next line if you'd  prefer getting the bricks set element id always
						  navigator.clipboard.writeText(placeholder);

						  // Uncomment this line if you want the bricks set element id
						  // navigator.clipboard.writeText(dataId);
					  });

				  }
			  }
		  };

	const observer = new MutationObserver(callback);

	observer.observe(targetNode, config);
}

document.addEventListener('DOMContentLoaded', function() {
	stuElementCopyId();
});
11 Likes

Thanks, i hope it will be added built-in in bricks builder
It is very helpful for custom css

1 Like

+1 really nice UX

please implement this soon

1 Like

Beautiful snippet. Thank you.

1 Like

Hey all,

There’s been a few bugs reported lately. Mainly it doesn’t work with custom ID’s. I can’t update my original reply because too much time has passed. I’ve included a link to a GitHub project that has a fix. I’ll include future fixes there as well:

4 Likes

Much thanks, mate :slight_smile:

@cmstew Thank you for you contribution. Much appreciated <3!

Here’s an updated version that handles active class as well:

And if you use a snippet plugin like Code Snippets, WPCodebox, etc. just plug this in a snippet:

Updated 3/24/23

<?php

add_action( 'wp_enqueue_scripts', function() {
	if ( bricks_is_builder_main() ) { ?>
        <script>
            document.addEventListener('DOMContentLoaded', () => {
                stuElementCopyElement();
            });

            function stuElementCopyElement() {
                const copyIdIcon = '<svg width="12" height="13" viewBox="0 0 12 13" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M4 1.549a.5.5 0 0 1 .5.5v2h3v-2a.5.5 0 0 1 1 0v2H10a.5.5 0 0 1 0 1H8.5v3H10a.5.5 0 0 1 0 1H8.5v2a.5.5 0 1 1-1 0v-2h-3v2a.5.5 0 1 1-1 0v-2H2a.5.5 0 0 1 0-1h1.5v-3H2a.5.5 0 0 1 0-1h1.5v-2a.5.5 0 0 1 .5-.5Zm3.5 6.5v-3h-3v3h3Z" fill="currentColor"/></svg>';
                const copyClassIcon = '<svg width="12" height="13" viewBox="0 0 12 13" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M8.146 3.695a.5.5 0 0 1 .708 0l2.5 2.5a.5.5 0 0 1 0 .707l-2.5 2.5a.5.5 0 1 1-.708-.707l2.147-2.146-2.147-2.147a.5.5 0 0 1 0-.707Zm-4.292 0a.5.5 0 0 1 0 .707L1.707 6.55l2.147 2.146a.5.5 0 1 1-.708.707l-2.5-2.5a.5.5 0 0 1 0-.707l2.5-2.5a.5.5 0 0 1 .708 0ZM7.108 1.56a.5.5 0 0 1 .38.597l-2 9a.5.5 0 0 1-.976-.217l2-9a.5.5 0 0 1 .596-.38Z" fill="currentColor"/></svg>';

                // Destructuring for efficient future use
                const { querySelector } = document;
                const { contentWindow } = document.getElementById('bricks-builder-iframe');

                // Create copy id icon
                const spanId = document.createElement('span');
                spanId.setAttribute('class', 'bricks-svg-wrapper stu-copy-id');
                spanId.setAttribute('data-balloon', 'Copy (Element ID)');
                spanId.setAttribute('data-balloon-pos', 'top-right');
                spanId.innerHTML = copyIdIcon;

                // Create copy class icon
                const spanClass = document.createElement('span');
                spanClass.setAttribute('class', 'bricks-svg-wrapper stu-copy-class');
                spanClass.setAttribute('data-balloon', 'Copy (Element Class)');
                spanClass.setAttribute('data-balloon-pos', 'top-right');
                spanClass.innerHTML = copyClassIcon;
                
                // Create style tag and append to head
                document.head.appendChild(document.createElement('style')).innerHTML = `
                    #bricks-panel-element-classes .active-class .actions :is(.stu-copy-id, .stu-copy-class) {
                            visibility: hidden;
                    }
                    #bricks-panel-element-classes .active-class:hover .actions :is(.stu-copy-id, .stu-copy-class) {
                            visibility: visible;
                    }
                `;

                // MutationObserver declarations
                const targetNode = document.querySelector('#bricks-panel');
                const config = { childList: true, subtree: true };

                // Callback function to detect DOM changes within #bricks-panel
                const callback = (mutationList, observer) => {
                    for (const mutation of mutationList) {
                        if (mutation.target.id === 'bricks-panel-element' || (mutation.addedNodes.length > 0 && mutation.addedNodes[0].id === 'bricks-panel-element')) {
                            const activeClass = document.querySelector('#bricks-panel-element-classes .active-class');
                            const placeholder = activeClass.querySelector('input').getAttribute('placeholder');
                            const bricksCanvas = contentWindow.document;
                            const dataId = bricksCanvas.querySelector(placeholder) ? bricksCanvas.querySelector(placeholder).getAttribute('data-id') : null;
                            const actionsContainer = activeClass.querySelector('div.actions');
                            const elementClass = document.querySelector('#bricks-panel-element-classes .element-classes .element-class.active > .name')?.innerText ?? null;
                            
                            if (elementClass) {
                                actionsContainer.prepend(spanId, spanClass);

                                const copyClass = actionsContainer.querySelector('span.stu-copy-class');
                                copyClass.addEventListener('click', (e) => {
                                    e.stopPropagation();
                                    navigator.clipboard.writeText(elementClass);
                                });
                            } else {
                                actionsContainer.prepend(spanId);

                                const copyId = actionsContainer.querySelector('span.stu-copy-id');
                                copyId.addEventListener('click', (e) => {
                                    e.stopPropagation();
                                    navigator.clipboard.writeText(placeholder);
                                });
                            }
                        }
                    }
                };
                
                // Create the MutationObserver object and monitor changes within #bricks-panel
                const observer = new MutationObserver(callback);
                observer.observe(targetNode, config);
            }
        </script>
    <?php }
} );
3 Likes

Hey Chris,

You’re very welcome. Thanks for the contribution. I’ll take a closer look this evening. :slightly_smiling_face:

1 Like

+1 for this feature, thanks for the custom code snippets team :slight_smile:

This isn’t working for me. I tried the latest code on your git page.

First element that I copy the ID for works fine but if I try to copy a second elements ID I still have the first element ID on my clipboard.

+1 for this addition. By copying the IDs/Classes this way we don’t need to manually type it(wrongly typed sometimes).

Great idea :ok_hand:

We could provide something like this:

bricks-copy-element-id-to-clipboard

So after clicking the new “copy” icon (not yet in Bricks) the value in the clipboard is #brxe-yfiosu.
Or better just brxe-yfiosu (without the leading #)?

Agreed on populating the existing element ID after you click “rename” like this:

bricks-rename-element-id-populate

9 Likes

With # is okay - most of the time anyway I think. Maybe even less support requests why # was not copied with it. :partying_face:

1 Like

It’s okay with #. Thomas.

1 Like

I’ll look into this. I never really copied the ID, but once just to test. I only use the copy class. So, I’ll definitely look into this and update it.

1 Like

Copying IDs usually used for triggers.

Ace thanks :slight_smile: It’s a really helpful tool, saves me squinting at the tiny, low contrast ID in the builder!

I’ve thoroughly tested the code and everything seems to work as it should. Should I just hide the copy ID when a class is selected? Would that be better UX?

Yes if there is no class selected best to hide the class copy icon, similarly with the ID? Hide the ID copy icon if a class is selected?

1 Like