JavaScript İle Basit BBCode Editörü ve Emoji Sistemi Yapımı

fatihege

Doçent
Katılım
27 Nisan 2020
Mesajlar
661
Reaksiyon puanı
398
Puanları
63
Konum
Kocaeli, Turkey
Coding-Geospatial.jpg


Yeniden herkese merhaba. :) Bu başlıkta, JavaScript kullanarak basit bir BBCode editörü nasıl yapılır bunu anlatıyor olacağım. Eğer sizde kullanmak isterseniz, projenize göre eklemeler ve özelleştirmeler yapabilirsiniz.

NOT: Bu yazıda olup bitenleri tam olarak anlayabilmeniz için temel seviye HTML, orta seviye CSS ve JavaScript bilgisine ihtiyacınız olacaktır.

Öyleyse HTML kodlarımız ile başlayalım.

HTML:
<div id="container">
    <div id="form">
        <div id="message-box">
            <div id="top">
                <div class="option" id="bb--bold" title="Bold">
                    <img src="https://svgur.com/i/SzK.svg" alt="Bold" class="top-bar--icon" ondragstart="javascript:return false;">
                </div>
                <div class="option" id="bb--italic" title="Italic">
                    <img src="https://svgur.com/i/T09.svg" alt="Italic" class="top-bar--icon" ondragstart="javascript:return false;">
                </div>
                <div class="option" id="bb--ul" title="Unordered List">
                    <img src="https://svgur.com/i/Syu.svg" alt="UL" class="top-bar--icon" ondragstart="javascript:return false;">
                </div>
                <div class="option" id="bb--ol" title="Ordered List">
                    <img src="https://svgur.com/i/Syv.svg" alt="OL" class="top-bar--icon" ondragstart="javascript:return false;">
                </div>
                <div class="option" id="bb--link" title="Link">
                    <img src="https://svgur.com/i/T0A.svg" alt="Link" class="top-bar--icon" ondragstart="javascript:return false;">
                </div>
                <div class="option" id="bb--align" title="Text Align">
                    <img src="https://svgur.com/i/Sxu.svg" alt="Align" class="top-bar--icon" ondragstart="javascript:return false;">
                    <ul class="dropdown-option vertical">
                        <li class="align-item" id="align-right" title="Align Right">Right</li>
                        <li class="align-item" id="align-center" title="Align Center">Center</li>
                        <li class="align-item" id="align-left" title="Align Left">Left</li>
                    </ul>
                </div>
                <div class="option" id="bb--emojis" title="Emojis">
                    <img src="https://svgur.com/i/SzW.svg" alt="Emojis" class="top-bar--icon" ondragstart="javascript:return false;">
                    <ul class="dropdown-option">
                        <li>
                            <img title="" src="https://twemoji.maxcdn.com/2/72x72/1f642.png" class="emoji-icon--dropdown" id="e-1" ondragstart="javascript:return false;">
                        </li>
                        <li>
                            <img title="" src="https://twemoji.maxcdn.com/2/72x72/1f604.png" class="emoji-icon--dropdown" id="e-2" ondragstart="javascript:return false;">
                        </li>
                        <li>
                            <img title="" src="https://twemoji.maxcdn.com/2/72x72/1f923.png" class="emoji-icon--dropdown" id="e-3" ondragstart="javascript:return false;">
                        </li>
                        <li>
                            <img title="" src="https://twemoji.maxcdn.com/2/72x72/1f643.png" class="emoji-icon--dropdown" id="e-4" ondragstart="javascript:return false;">
                        </li>
                        <li>
                            <img title="" src="https://twemoji.maxcdn.com/2/72x72/1f60d.png" class="emoji-icon--dropdown" id="e-5" ondragstart="javascript:return false;">
                        </li>
                    </ul>
                </div>
                <div class="option" id="bb--new-line" title="New Line">
                    <img src="https://svgur.com/i/Sz0.svg" alt="New Line" class="top-bar--icon" ondragstart="javascript:return false;">
                </div>
            </div>
            <div id="textarea-box">
                <textarea id="textarea" cols="30" rows="13"></textarea>
            </div>
            <button id="submit">Submit</button>
        </div>
    </div>
    <div id="output">
    </div>
</div>
Şimdi bu satırları açıklamak gerekirse; en başta ID’si "container" olan taşıyıcı bir element oluşturuyoruz ve onun altına da 2 adet yeni elementler oluşturuyoruz: "#form" ve "#output". "#form", bizim editörümüzün bulunacağı taşıyıcı element, "#output" ise sonucumuzun görüneceği element. "#form" elementinin içine ID’si "message-box" olan bir element oluşturuyoruz. Bu elementin içine BBCode’larımıza hızlıca erişeceğimiz kısayol butonlarımızın bulunacağı "#top" elementini, onun altına da yazıları yazacağımız alanın bulunacağı element olan "#textarea-box"ı ve onun da altına yazdığımız yazının sonuçlarını "#output" elementinde göstermek için basacağımız "#submit" butonu bulunuyor. Eğer HTML bilginiz varsa yazılanları daha iyi anlayacaksınızdır. "#top" elementinin altında çok fazla element olduğu için tek tek anlatmayacağım.

Şimdi CSS kodlarımıza geçebiliriz.

CSS:
/*- Inter -*/
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@100;200;300;400;500;600;700;800;900&display=swap');

/*- Fira Mono -*/
@import url('https://fonts.googleapis.com/css2?family=Fira+Mono:wght@400;500;700&display=swap');

* {
    margin: 0;
    padding: 0;
    outline: none;
    box-sizing: border-box;
}

::selection {
    background: rgba(50, 147, 237, 0.4);
}

::-webkit-scrollbar {
    width: 10px;
    height: 10px;
}

::-webkit-scrollbar-track {
    background: #e6e6e6;
}

::-webkit-scrollbar-thumb {
    background: #229ce3;
    border-radius: 100ch;
    cursor: pointer;
}

body {
    font-family: "Inter", sans-serif;
    overflow: auto;
}
Bu kodlar ile 2 adet font ailesini CSS dosyamıza dahil ediyor; bütün elementlerin "margin" ve "padding" değerlerini sıfırlayıp "outline"ı kaldırıyoruz. Sonrasında metin seçimi yapıldığında tarayıcının varsayılan arkaplan rengini görmek yerine kendi arkaplan rengimizi tanıtıyoruz. Ardından, tarayıcının varsayılan scrollbar’ını kendimiz özelleştiriyoruz ve "<body>" etiketi arasında olan bütün elementlerin yazı tipini “Inter” yapıp, herhangi bir taşma durumunda otomatik olarak tepki verilmesi için "overflow" özelliğine "auto" değerini veriyoruz.

CSS:
#container {
    width: 100%;
    height: 100vh;
    min-width: max-content;
    background: efefef;
    display: flex;
    justify-content: center;
    align-items: center;
    flex-direction: column;
}
Bu bloğumuzda; ID’si "container" olan elementimizin genişlik, yükseklik, arkaplan ve görüntüleme gibi özelliklerini değiştiriyoruz.

CSS:
#form {
    width: 50%;
    height: max-content;
    background: #0ff;
    position: relative;
    border-radius: 15px;
    min-width: 700px !important;
}
Bu blokta; ID’si "form" olan 2. taşıyıcı elementimizin genişlik, yükseklik, konumlandırma ve kenar ovalliği gibi değerlerini ayarlıyoruz.

CSS:
.option .top-bar--icon {
    width: 25px;
    height: auto;
}
Bu blokta; üstteki HTML kodumuzda da gördüğünüz, ID’si "top" olan taşıyıcı elementimizin içerisinde bulunan ve bizim BBCode’ları eklememizi kolaylaştıracak olan kısayollarımızın içindeki resimlerin boyutunu ayarlıyoruz.

CSS:
textarea#textarea {
    width: 100%;
    height: 100%;
    border-bottom-right-radius: 15px;
    border-bottom-left-radius: 15px;
    resize: none;
    border-top-right-radius: 0;
    border-top-left-radius: 0;
    transition: box-shadow 0.2s;
    font-family: "Fira Mono", monospace;
    padding: 10px;
    font-size: 16px;
    border: 1px solid #a8a8a8;
}
Bu blokta ise; ID’si "textarea" olan "<textarea>" etiketimizin genişlik, yükseklik, boyutlandırma, kenar ovalliği, yazı tipi, içten öteleme, kenar çizgisi ve dönüşüm özelliklerini ayarlıyoruz.

CSS:
textarea#textarea:focus {
    box-shadow: 0px 0px 0px 5px rgba(50, 147, 237, 0.4);
}
Bu blokta ise; ID’si "textarea" olan "<textarea>" etiketimize odaklanıldığında açık mavi renkli bir kenarlık gibi görünecek olan gölgemizi oluşturuyoruz.

CSS:
#form #top {
    width: 100%;
    height: max-content;
    background: linear-gradient(90deg, #11a2f0, #0935ad);
    display: flex;
    flex-direction: row;
    justify-content: space-evenly;
    border-top-right-radius: 15px;
    border-top-left-radius: 15px;
    padding: 20px 0px;
}
Bu bölümde; "#form" elementimizin içindeki "#top" elementinin genişlik, yükseklik, arkaplan rengi, görüntüleme ve içten öteleme gibi özelliklerine istediğimiz değerleri veriyoruz.

CSS:
#top .option {
    padding: 10px;
    border-radius: 10px;
    margin-left: 10px;
    background: #fff;
    cursor: pointer;
    transition: border 0.2s;
    display: flex;
    justify-content: center;
    align-items: center;
    transition: transform 0.2s;
}
Bu blokta; "#top" elementimizin içinde bulunan ve class’ı "option" olan bütün elementlerimize ortak özellikleri veriyoruz.

CSS:
#top .option:first-child {
    margin-left: 0;
}
Bu blokta; "#top" elementinin içinde yer alan ve class’ı "option" olan ilk elementimizin soldan öteleme değerini sıfırlıyoruz ki, orantısızlık olmasın.

CSS:
#top .option:hover {
    transform: scale(1.1);
}

#top .option:active {
    transform: scale(0.9);
}
Burada; "#top" elementimizin içerisindeki "option" class’ına sahip bütün elementlerin üzerine gelindiğinde ve tıklandığında dönüşüm değerleriyle oynuyoruz.

CSS:
#output {
    min-width: 700px;
    padding: 15px;
    font-size: 17px;
    border: 1px solid #a8a8a8;
    border-radius: 15px;
    margin-top: 4px;
}
Bu blokta; ID’si "output" olan elementimize gereken görünüş özelliklerini veriyoruz.

CSS:
#output ul, #output ol {
    padding-left: 15px;
}
Bu blokta; BBCode ile oluşturulacak olan "<ul>" ve "<ol>" etiketine sahip olan liste elementlerinin solunda bulunan liste simgelerinin, "#output" elementinin sol kenarı ile olan bağını kesmek için sol taraftan içten öteleme veriyoruz. Ne dediğimi tam olarak anlamak için bu kodları yorum satırına alıp aradaki farkı gözlemleyebilirsiniz.

CSS:
button#submit {
    width: 100%;
    height: auto;
    font-size: 20px;
    border-radius: 15px;
    color: #fff;
    background: linear-gradient(90deg, #11a2f0, #0935ad);
    border: none;
    padding: 15px;
    cursor: pointer;
    transition: 0.2s;
    position: relative;
    font-family: "Inter", sans-serif;
    font-weight: 900;
    box-shadow: 0px 0px 0px 0px rgba(24, 116, 242, 0.6);
}

button#submit:focus {
    box-shadow: 0px 0px 0px 5px rgba(24, 116, 242, 0.6);
}
Bu blokta; ID’si "submit" olan butonumuza istediğimiz görünüş özelliklerini veriyori ve butona odaklanıldığında kenarında bir kalın bir çizgiymiş gibi görünecek olan gölgeyi ortaya çıkarıyoruz.

CSS:
ul.dropdown-option {
    cursor: default;
    width: max-content;
    position: absolute;
    list-style-type: none;
    display: flex;
    justify-content: center;
    flex-direction: row;
    align-items: center;
    background: #fff;
    padding: 15px;
    border-radius: 15px;
    box-shadow: 0px 0px 10px rgba(92, 92, 92, 0.5);
    opacity: 0;
    transition-duration: 0.3s;
    transition-property: opacity, margin, transform;
    transform: scale(0.2);
}

ul.dropdown-option.vertical {
    flex-direction: column;
}

ul.dropdown-option .emoji-icon--dropdown {
    max-width: 25px;
    margin-left: 10px;
    transition: transform 0.2s;
}

ul.dropdown-option .emoji-icon--dropdown:hover {
    transform: scale(1.1);
}

ul.dropdown-option::before {
    content: "";
    border: 10px solid transparent;
    border-bottom-color: #fff;
    position: absolute;
    top: -10px;
    transition: top 0.3s;
}

.option:hover ul.dropdown-option {
    overflow: visible;
    opacity: 1;
    width: auto;
    transform: scale(1);
    margin-bottom: -130px;
}

.option:hover ul.dropdown-option.vertical {
    margin-bottom: -180px;
}

.option:hover ul.dropdown-option::before {
    top: -19px;
}

.option ul.dropdown-option li {
    cursor: pointer;
}

#bb--emojis:active, #bb--align:active {
    transform: scale(1.1) !important;
}

#bb--align ul.dropdown-option .align-item {
    margin-bottom: 10px;
    transition: transform 0.2s;
}

#bb--align ul.dropdown-option .align-item:last-child {
    margin-bottom: 0;
}

#bb--align ul.dropdown-option .align-item:hover {
    transform: scale(1.1);
}
Bu kodlarda, “Align” ve “Emoji” seçeneklerinin dropdown menülerine gereken stilleri veriyoruz.

Şimdi JavaScript kodlarımıza geçelim. Gerektiği zaman CSS kodlarına ekleme yapmaya devam edeceğiz.

JavaScript:
function $(elem) {
    return document.querySelector(elem);
}
İlk önce böyle bir fonksiyon oluşturuyoruz. Peki bu fonksiyon ne işe yarıyor? Eğer jQuery bilginiz varsa muhtemelen anlayacaksınızdır, biz bir elementi seçerken uzun uzun "document.querySelector()" yazmak yerine sadece "$()" yazarak aynı kodun karşılığını alacağız. Bu da bize vakitten kazanç sağlayacak.

JavaScript:
var msgBox = $("textarea#textarea");
var outputBox = $("#output");

var bbBold = $("#bb--bold");
var bbItalic = $("#bb--italic");
var bbUl = $("#bb--ul");
var bbOl = $("#bb--ol");
var bbLink = $("#bb--link");
var bbNewLine = $("#bb--new-line");

var alignRight = $("#bb--align #align-right");
var alignCenter = $("#bb--align #align-center");
var alignLeft = $("#bb--align #align-left");

var emoji1 = $("#bb--emojis #e-1");
var emoji2 = $("#bb--emojis #e-2");
var emoji3 = $("#bb--emojis #e-3");
var emoji4 = $("#bb--emojis #e-4");
var emoji5 = $("#bb--emojis #e-5");

var submitBtn = $("button#submit");
Bu satırlarda, kullanacağımız elementleri değişkenlere atıyoruz.

JavaScript:
bbBold.addEventListener("click", function () {
    msgBox.value = msgBox.value += "[bold][/bold]";
});
Bu blokta; kalın bir yazı yapmak istediğimizde kullanılacak olan BBCode’u "msgBox"ın değerine ekliyoruz. Bu kodun ne olduğunu anlattık. Şimdi geri kalanları vereyim. Bunu anladıysanız aşağıdakileri anlamanız çok zor olmayacak.

JavaScript:
bbItalic.addEventListener("click", function () {
    msgBox.value = msgBox.value += "[italic][/italic]";
});

bbUl.addEventListener("click", function () {
    msgBox.value = msgBox.value += "[ul]\n[li][/li]\n[/ul]";
});

bbOl.addEventListener("click", function () {
    msgBox.value = msgBox.value += "[ol]\n[li][/li]\n[/ol]";
});

bbLink.addEventListener("click", function () {
    msgBox.value = msgBox.value += '[link="http://"][/link]';
});

bbNewLine.addEventListener("click", function () {
    msgBox.value = msgBox.value += "[nl]";
});

// Aligns

alignRight.addEventListener("click", function () {
    msgBox.value = msgBox.value += "[align=right][/align]";
});

alignCenter.addEventListener("click", function () {
    msgBox.value = msgBox.value += "[align=center][/align]";
});

alignLeft.addEventListener("click", function () {
    msgBox.value = msgBox.value += "[align=left][/align]";
});

// Emojis

emoji1.addEventListener("click", function () {
    msgBox.value = msgBox.value += ":)";
});

emoji2.addEventListener("click", function () {
    msgBox.value = msgBox.value += ":D";
});

emoji3.addEventListener("click", function () {
    msgBox.value = msgBox.value += "XD";
});

emoji4.addEventListener("click", function () {
    msgBox.value = msgBox.value += "(:";
});

emoji5.addEventListener("click", function () {
    msgBox.value = msgBox.value += ":perfect:";
});
Burada olanları tek tek inceleyerek çok da zorlanmadan anlayabileceğinizi düşünüyorum.

JavaScript:
submitBtn.addEventListener("click", function () {
    outputBox.innerHTML = msgBox.value;

    outputBox.innerHTML = outputBox.innerHTML.replaceAll("[bold]", '<span class="bold">')
            .replaceAll("[/bold]", "</span>")
            .replaceAll("[italic]", '<span class="italic">')
            .replaceAll("[/italic]", "</span>")
            .replaceAll('[link="', '<a target="_blank" href="')
            .replaceAll('"]', '">')
            .replaceAll("[/link]", "</a>")
            .replaceAll("[li]", "<li>")
            .replaceAll("[/li]", "</li>")
            .replaceAll("[ul]", "<ul>")
            .replaceAll("[/ul]", "</ul>")
            .replaceAll("[ol]", "<ol>")
            .replaceAll("[/ol]", "</ol>")
            .replaceAll("[nl]", "<br/>")
            .replaceAll("[align=right]", '<p class="align-right">')
            .replaceAll("[align=center]", '<p class="align-center">')
            .replaceAll("[align=left]", '<p class="align-left">')
            .replaceAll("[/align]", "</p>")
            .replaceAll(':)', '<img src="https://twemoji.maxcdn.com/2/72x72/1f642.png" class="emoji-icon" />')
            .replaceAll(':D', '<img src="https://twemoji.maxcdn.com/2/72x72/1f604.png" class="emoji-icon" />')
            .replaceAll('XD', '<img src="https://twemoji.maxcdn.com/2/72x72/1f923.png" class="emoji-icon" />')
            .replaceAll('(:', '<img src="https://twemoji.maxcdn.com/2/72x72/1f643.png" class="emoji-icon" />')
            .replaceAll(':perfect:', '<img src="https://twemoji.maxcdn.com/2/72x72/1f60d.png" class="emoji-icon" />');
});
Ve işte en önemli bölüme geldik. Bu bölümü baştan aşağıya anlayacağınız bir şekilde özetlemeye çalışacağım. Eğer aklınıza takılan bir yer olursa sorabilirsiniz. :) En temelinde; “Submit” butonuna tıklandığında gerçekleşecek olan eylemleri belirliyoruz.

"outputBox.innerHTML = msgBox.value;" ile, "outputBox"ın değerini, editörümüzün değeri ile eşleştiriyoruz. Yani, editörde ne yazıyorsa "outputBox"a geçiyor.

Sonraki kodlarda bulunan mantığı anlatacak olursak; örneğin "outputBox.innerHTML = outputBox.innerHTML.replaceAll("[bold]", '<span class="bold">').replaceAll("[/bold]", "</span>")" bölümünde, eğer "outputBox"ın içinde “[bold]” bloğu geçiyorsa, onu "<span class="bold">" değeri ile değiştiriyoruz. Sonrasında, eğer “[/bold]” bloğu varsa, onu "</span>" kapanış etiketi ile değiştiriyoruz. Temel olarak böyle. Burada dikkat etmeniz gereken nokta, "replaceAll()" fonksiyonunu kullanıyoruz. Bu fonksiyon ile, verdiğimiz 1. parametrede yer alan bütün değerleri, verdiğimiz 2. parametrede yer alan değerler ile değiştiriyoruz. Eğer sadece "replace()" fonksiyonunu kullansaydık, önüne çıkan ilk BBCode’u değiştireceği için sistem tam olarak çalışmayacaktı.

Şimdi tekrar CSS'e dönelim. Yukarıda "replaceAll()" fonksiyonunu kullanarak değiştirdiğimiz bloklara dikkat ederseniz ne yapmak istediğimi daha rahat anlayacaksınızdır.

CSS:
/*- BBCodes -*/

span.bold {
    font-weight: 900;
}

span.italic {
    font-style: italic;
}

p.align-right {
    text-align: right;
}

p.align-center {
    text-align: center;
}

p.align-left {
    text-align: left;
}
Bu kodlarda, yukarıdaki JavaScript kodlarında "replaceAll()" fonksiyonunu kullanarak değiştirdiğimiz bloklara gereken stil özelliklerini veriyoruz.

CSS:
/*- Emojis -*/

img.emoji-icon {
    max-width: 17.5px;
    height: auto;
    cursor: pointer;
}
Bu blokta da; class’ı "emoji-icon" olan "<img>" etiketlerimize genişlik, yükseklik gibi özellikleri veriyoruz.

Hepsi bu. :) Eğer aklınıza takılan bir yer olursa veya hata alırsanız benimle iletişime geçebilirsiniz. Şunu da eklemek istiyorum; ben sadece responsive olmayan editörün kodlarını verdim, yani en temel kodları yazdım. Eğer kullanacaksanız geri kalan özelleştirmeleri yapmak sizin elinizde.

Hazır kodlar: CodePen.io/Pen/
Çıktı: CodePen.io/Full/

Ekran görüntüsü:
Screenshot_1.png
 
Son düzenleme:
Üst