Inkunabuły mądrości

blog.comandeer.pl 19 godzin temu

Po odświeżeniu dygresji przyszedł czas na kolejny częsty element mojego bloga: bloczki z kodem!

Stare bloczki

Stare bloczki… były:

Przykład bloczka kodu z wpisu Frywolne marginalia

Co prawda spełniały swoje zadanie, miały choćby dobre kolorowanie składni (dzięki Shiki), ale brakowało w nich choćby informacji o języku programowania. Dlatego też postanowiłem je nieco odświeżyc.

Odświeżone bloczki

Nowe bloczki wyglądają następująco:

Przykład odświezonego bloczka kodu z wpisu Frywolne marginalia

Tak naprawdę zmiany nie są szczególnie wielkie – pojawiła się jedynie nazwa języka oraz przycisk do skopiowania kodu do schowka.

Nowy HTML

Od strony HTML-a cały bloczek kodu trafił do elementu figure:

HTML Kopiuj
<figure class="code" typeof="SoftwareSourceCode"> <figcaption class="code__caption"> <!-- 3 --> <span class="code__title" property="programmingLanguage">CSS</span> <!-- 1 --> <button class="code__copy">Kopiuj</button> <!-- 2 --> </figcaption> <div class="code__code" translate="no" property="text"> <!-- 4 --> <pre><code>[…]</code></pre> <!-- 5 --> </div> </figure>

Podpis z nazwą języka (1) i przyciskiem (2) został umieszczony w figcaption (3), natomiast sam kod w dodatkowym divie z atrybutem [translate=no] (4). Dzięki temu Google Translate i podobne usługi nie będą próbowały tłumaczyć kodu na inne języki. Dodatkowo zastosowałem też RDFa, żeby oznaczyć całość jako kod w języku programowania. Sam kod (5) pozostał bez zmian – dalej jest kolorowany przez Shiki.

Nowy JS

Przycisk do kopiowania kodu potrzebuje obsługi w JS-ie. Dodałem więc taką do swojego pliku JS:

JavaScript Kopiuj
document.addEventListener( 'click', async ( evt ) => { // 1 const isCopyButton = evt.target.closest( '.code__copy' ); // 2 if ( !isCopyButton ) { return; } const closestCode = evt.target.closest( '.code' ).querySelector( '.code__code' ); // 3 await navigator.clipboard.writeText( closestCode.innerText ); // 4 } );

Stosuje tutaj technikę event delegation (delegacji zdarzeń) (1). Dzięki temu mogę przypiąć tylko jeden nasłuchiwacz i obsłużyć wszystkie przyciski “Kopiuj”, zamiast musieć się przypiąć do każdego z nich osobno. Niemniej ta technika sprawia, ze muszę sprawdzić, czy na pewno został kliknięty przycisk “Kopiuj”. Robię to przy pomocy wyszukania go przez metodę #closest() (2). Metoda ta zwróci najbliższy element o klasie .code__copy. W przypadku, gdy ktoś kliknie przycisk, będzie to on sam. Metoda #closest() zabezpiecza nas przed przypadkiem, gdybyśmy dodali kiedyś ikonkę do przycisku i ktoś kliknął w nią – wówczas zamiast przycisku mielibyśmy w evt.target np. element svg. Jak już ustalimy, iż został naciśnięty odpowiedni przycisk, wyszukujemy najbliższy bloczek kodu (3). Następnie wywołujemy metodę Clipboard#writeText(), do której przekazujemy tekstową zawartość bloczka (4). jeżeli osoba naciskająca przycisk wyrazi zgodę na zapisanie danych do schowka, kod zostanie skopiowany.

Nowy backend

Nowe bloczki kodu renderowane są przy pomocy autorskiego pluginu do MarkdownIt (biblioteki renderującej Markdowna w Eleventy). Jego kod prezentuje się następująco:

JavaScript Kopiuj
function markdownItCodeBlock( markdownIt ) { const originalFenceRule = markdownIt.renderer.rules.fence; // 1 markdownIt.renderer.rules.fence = ( tokens, idx, options, env, slf ) => { const token = tokens[ idx ]; const lang = token.info ? token.info.trim() : ''; // 3 const renderedCodeBlock = originalFenceRule( tokens, idx, options, env, slf ); // 4 / * 5 */ return `<figure typeof="SoftwareSourceCode"> <figcaption> <span property="programmingLanguage">${ langs[ lang ] ?? '' }</span> <button>Kopiuj</button> </figcaption> <div translate="no" property="text"> ${ renderedCodeBlock } </div> </figure>`; }; }

Na początku zapisujemy sobie do zmiennej aktualną funkcję do renderowania bloczków kodu (1). W przypadku MarkdownIt funkcja ta znajduje się w regułach renderera. Następnie nadpisujemy tę regułę własną (2). Wyciągamy z kodu Markdown nazwę języka programowania (3), a następnie wywołujemy oryginalną regułę renderującą bloczki kodu (4). Dzięki temu dostajemy kod ładnie pokolorowany przez Shiki. Na koniec tworzymy nasz własny kod HTML bloczka (5), do którego wsadzamy pokolorowany kod i zwracamy całość z funkcji.

Teraz zostaje tylko dodać ten plugin do naszej zmodyfikowanej wersji MarkdownIt. Można to zrobić przy pomocy metody #use().

I to tyle! Bloczki kodu na blogu są od dzisiaj nieco lepsze.

Idź do oryginalnego materiału