Підсвічування коментарів

Функції JScript і стилізація CSS коментарів для блоків коду в статтях з темною темою. Реалізовано можливість копіювання множинних блоків коду на одній сторінці.

    Виокремлення коментарів

    Стилізація коментарів залежить від типу кодового блоку і визначається CSS-класами, що додаються.

    /* Основні стилі для контейнера з кодом*/
    
    .code-container {
        font-family: "Fira Code", monospace;
        background-color: #2d2d2d;
        color: #ccc;
        padding: 10px;
        border-radius: 5px;
        white-space: pre-wrap;
        overflow-x: auto;
    }
    .html-comment {
        color: #6a9955;
        font-style: italic;
    }
    .css-comment {
        color: #6bbc45;
        font-style: italic;
    }
    .js-comment {
        color: #808080;
        font-style: italic;
    }
    .hash-comment {
        color: #b5cea8;
        font-style: italic;
    }
    .brace {
        color: #6bbc45;
        font-weight: bold;
    }
    

    Основний скрипт розбирає текст контейнера за допомогою регулярного виразу, розділяє коментарі за типом (HTML, CSS, JS) і огортає відповідними класами CSS для їх колоризації.

    function highlightComments(container) {
        if (container.classList.contains('highlighted')) return;
        container.classList.add('highlighted');
    
        const code = container.textContent;
        const fragment = document.createDocumentFragment();
    
        // Регулярний вираз
        const parts = code.split(
            /(https?:\/\/[^\s]+|<!--[\s\S]*?-->|\/\*[\s\S]*?\*\/|\/\/.*$|(^|\s)#(?![a-fA-F0-9]{3,6}\b).*|[{}])/gm
        ).filter(Boolean);
    
        parts.forEach(part => {
            const span = document.createElement('span');
            let matched = false;
    
            if (/^https?:\/\/[^\s]+$/.test(part)) {
                span.className = 'url';
            } else if (/^<!--[\s\S]*?-->$/.test(part)) {
                span.className = 'html-comment';
            } else if (/^\/\*[\s\S]*?\*\/$/.test(part)) {
                span.className = 'css-comment';
            } else if (/^\/\/.*$/.test(part)) {
                span.className = 'js-comment';
            } else if (/^(\s*)#(?![a-fA-F0-9]{3,6}\b).*$/.test(part)) {  
                // # коментар тільки якщо не перед кольором
                span.className = 'hash-comment';
            } else if (/^[{}]$/.test(part)) {
                span.className = 'brace';
            } else {
                span.textContent = part;
                fragment.appendChild(span);
                return;
            }
    
            span.textContent = part;
            fragment.appendChild(span);
        });
    
        container.innerHTML = '';
        container.appendChild(fragment);
    }
    
    // Ініціалізація підсвічування для всіх контейнерів з класом .code-container
    function setupHighlighting() {
    	document.querySelectorAll('.code-container').forEach(container => {
    		highlightComments(container);
    	});
    }
    
    // Виклик підсвічування під час завантаження сторінки
    window.addEventListener('load', setupHighlighting);
    

    Для додавання властивостей підсвічування коментарів, слід лише прописати клас code-container у відповідних тегах. Варто нагадати ще раз: властивість розповсюджується на текст лише у межах наступного блоку, до якого прописаний клас.

    <pre class="code-container">
    	<!--  HTML .html-comment -->
    	/* CSS .css-comment */
    	// JS .js-comment
    	# Hash .hash-comment
    	
    	$100  {...}
    </pre>
    

    Копіювання блоків коду

    Пропонуються дві функції, які виконують одну й ту ж дію - копіювання в буфер обміну, але по-різному виконують це на сторінці в браузері. Перший спосіб є традиійним, з використанням кнопки копіювання тексту, а другий здійснює копіювання після кліку на відповідному блоці без відображення додаткових елементів.

    З допомогою кнопки

    У функції copyToClipboard використовується button.nextElementSibling для пошуку наступного елемента, який відповідає блоку тексту. Така прив'язка, яка не задіює id для ідентифікації елемента та виконується автоматично.

    // Копіювання тексту в буфер обміну через кнопку
    
    function copyToClipboard(button) {
    	const container = button.nextElementSibling; // Знаходження наступного блоку коду
    	const code = container.textContent;
    
    	// Створення виділення для копіювання
    	const range = document.createRange();
    	range.selectNodeContents(container);
    	const selection = window.getSelection();
    	selection.removeAllRanges();
    	selection.addRange(range);
    
    	navigator.clipboard.writeText(code) // Копіювання тексту
    		.then(() => {
    			alert('Код успішно скопійовано в буфер обміну!');
    			setTimeout(() => selection.removeAllRanges(), 500); // Скинути виділення
    		})
    		.catch(err => console.error('Помилка копіювання:', err));
    }
    

    Для додавання в HTML, слід прописати клас class="copy-button" та локатор події onclick="copyToClipboard(this)" у тегах самої кнопки. Не слід забувати, що: властивість розповсюджується на текст лише у межах наступного блоку, до якого відноситься кнопка.

    <!-- Демо копіювання в HTML -->
    
    <button class="copy-button" onclick="copyToClipboard(this)">Копіювати</button>
    <pre>.. тут текст .. </pre>				
    

    З допомогою виділення

    У функції copyCode використовується element.parentElement для пошуку наступного елемента, який відповідає блоку тексту. Така прив'язка, яка не задіює id для ідентифікації елемента та виконується автоматично.

    // Копіювання тексту в буфер обміну через виділення
    
    function copyCode(element) {
    	const codeContainer = element.parentElement; // Знайти батьківський тег
    	const code = codeContainer.textContent.trim();
    
    	// Виділення тексту
    	
    	const range = document.createRange();
    	range.selectNodeContents(codeContainer);
    	const selection = window.getSelection();
    	selection.removeAllRanges();
    	selection.addRange(range);
    
    	// Копіювання тексту
    	
    	navigator.clipboard.writeText(code)
    		.then(() => {
    			alert('Код успішно скопійовано в буфер обміну!');
    			setTimeout(() => selection.removeAllRanges(), 500); // Скинути виділення
    		})
    		.catch(err => console.error('Помилка копіювання:', err));
    }			
    

    Для додавання в HTML, слід прописати клас class="code-container" та локатор події onclick="copyCode(this)" у відповідних тегах. Не слід забувати, що: властивість розповсюджується на текст лише у межах наступного блоку, до якого відноситься кнопка.

    <!-- Демо копіювання в HTML -->
    
    <pre class="code-container" style="cursor: pointer" title="Натисніть, щоб скопіювати" onclick="copyCode(this)">
    	.. тут текст .. 
    </pre>				
    

    Тепер, навівшись на блок з текстом, і зробивши на ньому лівий клік, отримаємо його вміст буфері обміну. Додатково, можна візалізувати дію додавши вказівник style="cursor: pointer" та спливаючу тайтл-підказку.

    Функція згортання/розгортання блоку

    Задля економії місця на сторінці - простий варіант незалежних блоків тексту, з використанням nextElementSibling для пошуку наступного.

    // Функція згортання/розгортання блоку
    
    function togglePre(button) {
    	const wrapper = button.nextElementSibling; // Відповідний контейнер
    	const isHidden = wrapper.style.display === 'none';
    
    	wrapper.style.display = isHidden ? 'block' : 'none'; // Перемикання видимості
    	button.textContent = isHidden ? 'Сховати демо' : 'Показати демо';
    }
    
    // Додавання обробників подій для кнопок згортання/розгортання
    
    window.addEventListener('load', function () {
    	document.querySelectorAll('.toggle-button').forEach(button => {
    		button.addEventListener('click', () => togglePre(button));
    	});
    });
    

    Оформлення згортання/розгортання в HTML