25 Şubat 2016 Perşembe

Javascript Düzenli İfadeler

Bir karakter dizisi içinde istediğimiz ölçülere uygun bir alt karakter dizisi olup olmadığını veya karakter dizisinin tamamının istediğimiz ölçülere uygun olup olmadığını denetlemek için düzenli ifadeler kullanırız. Ayrıca karakter dizisi üzerinde bir çırpıda değişiklikler de yapabiliriz. Düzenli ifadeler hemen hemen tüm programlama dillerinde vardır. Javascript'te biraz daha sade olmakla birlikte işimizi görmeye yeterlidir. Düzenli ifadelerde temel amaç eşleşme kontrolüdür. Elimizde bir karakter dizisi vardır ve bunun başka bir karakter dizisi içinde olup olmadığına bakarız. Yani düzenli ifadeler tamamen karakter dizileri ile ilgilenir. İlk yapacağımız iş neyi arayacağımızı belirtmek. Yani bir düzenli ifade kalıbı oluşturmak.

var aranan = new RegExp('ne arıyorsun?');

Düzenli ifadelerin javascript karşılığı RegExp nesnesidir. Ama bu tanımlamanın daha düz bir yapısı vardır.

var aranan = /ne arıyorsun?/;

Ve genelde bu yapı kullanılır. İki tanımlama da aynı kapıya çıkar. Biz de bu ikinci yapıyı kullanacağız. Gördüğün gibi iki bölü işareti arasına ne aradığımızı yazıyoruz. Bu yapının kaynaklardaki tanımı şöyledir.

/pattern/modifiers;

pattern düzenli ifade kalıbı oluyor(iki bölü işareti arasında), yani ne arıyorsak o. modifiers ise arama yaparken, aramanın nasıl yapılacağı ile ilgili bir kaç yardımcı anahtar harf(g,i,m). Birazdan göreceğiz. Karakter dizilerinin metotlarından olan split(), search(), match() ve replace() metotları parametre olarak hem normal bir karakter dizisi, hem de düzenli ifade kalıbı alabilir. Önce en basitinden replace metodunun kullanımına bakalım.

var aranan =  'gün';
var metin = "sensiz geçen günleri ben günden saymıyorum.";

var n = metin.replace(aranan, 'dün');

console.log(n);       // sensiz geçen dünleri ben günden saymıyorum.
console.log(metin);   // sensiz geçen günleri ben günden saymıyorum.

Aradığımız karakter dizisini normal bir şekilde tanımladık, gün. Ve bunu dün ile değiştirmek istediğimizi belirttik. replace metodu da metin içinde bulduğu ilk gün kelimesini değiştirdi ve metnin değişiklik yapılmış halini döndürdü. Çünkü javascript'e karakter dizileri değiştirilemeyen bir veri türüdür. Bu yüzden bir karakter dizisi üzerinde değişiklik yapan tüm metotlar, değişikliği içeren yeni bir karakter dizisi döndürür. replace metodunu normalde böyle kullanıyoruz. Ama peki biz diğer gün kelimesini de değiştirmek istiyorsak? Bunu en basitinden bir döngü kurarak yapabiliriz. Ama bu şekilde metnin içinde nerede kaldığımızı bilmek için bir index değeri de tutmamız gerekir. Ayrıca değiştireceğimiz kelime metnin içinde büyük küçük harf karışık geçiyorsa bu kez döngüye if deyimleri de kaymamız gerekecek. Çoğu çok muhterem programcı amcalarımız der ki, eğer normal yoldan yapabiliyorsan düzenli ifadelere hiç bulaşma. Fakat artık çok geç, ilk düzenli ifade kalıbımızı oluşturuyoruz.

var aranan =  /gün/;
var metin = "sensiz geçen günleri ben günden saymıyorum.";

var n = metin.replace(aranan, 'dün');

console.log(n);       // sensiz geçen dünleri ben günden saymıyorum.
console.log(metin);   // sensiz geçen günleri ben günden saymıyorum.

Sonuç öncekiyle aynı. Çünkü farklı bir sonuç elde edecek bir işaretimiz yok. Henüz.

Arama Anahtarları
Anahtar Anlamı
g Global arama anahtarı. Karakter dizisinin tamamını kontrol etmek için.
i insensitive arama anahtarı. Karakter dizisinde büyük küçük harf ayırımı yapmaz.
m multiline arama anahtarı. Karakter dizisindeki tüm satırları aramak için.

Bu bilgiler ışığında, diğer gün kelimesinin geçtiği yeri de değiştirmek için global arama anahtarını kullanmamız gerek.

var aranan =  /gün/g;
var metin = "sensiz geçen günleri ben günden saymıyorum.";

var n = metin.replace(aranan, 'dün');

console.log(n);       // sensiz geçen dünleri ben dünden saymıyorum.
console.log(metin);   // sensiz geçen günleri ben günden saymıyorum.

Düzenli ifade kalıbımız daima iki bölü işareti arasında olacak. Ve bunun hemen ardına da yardımcı arama anahtarlarını belirteceğiz. İşin içine büyük küçük harfler de girerse i anahtarını da belirtmemiz gerek.

var aranan =  /gün/gi;
var metin = "sensiz geçen günleri ben GüNdEn saymıyorum.";

var n = metin.replace(aranan, 'dün');

console.log(n);       // sensiz geçen dünleri ben dündEn saymıyorum.
console.log(metin);   // sensiz geçen günleri ben günden saymıyorum.

Çok satırlı metinlerde tüm satırları kontrol etmek için m multiline anahtarını kullanıyoruz. Çok satırlı dediğimiz şey \n karakteri ile bölünmüş satırlardır. Fakat bu multiline anahtarı biraz garip çalışır. Tek başına kullanıldığında hiçbir etkisi yoktur.

var aranan =  /gün/m;
var metin = "sensiz geçen günleri ben GüNdEn saymıyorum düzgün\nBu günler yalancı günler\nkötüye gidiyor günbegün";

var n = metin.replace(aranan, 'dün');

console.log(n);
/* sensiz geçen dünleri ben GüNdEn saymıyorum düzgün
   Bu günler yalancı günler
   kötüye gidiyor günbegün*/

Eğer burada m multiline anahtarını belirtmeseydik de aynı sonucu alacaktık.

var aranan =  /gün/;
var metin = "sensiz geçen günleri ben GüNdEn saymıyorum düzgün\nBu günler yalancı günler\nkötüye gidiyor günbegün";

var n = metin.replace(aranan, 'dün');

console.log(n);
/* sensiz geçen dünleri ben GüNdEn saymıyorum düzgün
   Bu günler yalancı günler
   kötüye gidiyor günbegün*/

Üstelik burada g global anahtarını kullanırsak tüm satırlara ulaşabiliyoruz.

var aranan =  /gün/g;
var metin = "sensiz geçen günleri ben GüNdEn saymıyorum düzgün\nBu günler yalancı günler\nkötüye gidiyor günbegün";

var n = metin.replace(aranan, 'dün');

console.log(n);
/* sensiz geçen dünleri ben GüNdEn saymıyorum düzdün
   Bu dünler yalancı dünler
   kötüye gidiyor dünbedün*/

Madem g anahtarı tüm satırlara ulaşıyor, m anahtarı ne için? Bunu birazdan daha daha iyi anlayacağız.

var aranan =  /gün$/;
var metin = "sensiz geçen günleri ben GüNdEn saymıyorum düzgün\nBu günler yalancı bugün\nkötüye gidiyor günbegün";

var n = metin.replace(aranan, 'dün');

console.log(n);
/* sensiz geçen günleri ben GüNdEn saymıyorum düzgün
   Bu günler yalancı bugün
   kötüye gidiyor günbedün*/

Düzenli ifade kalıbımızda yeni bir karakter var. Bu karakter bir konum karakteridir ve bu şekilde javascript yorumlayıcısına, aradığımız şeyi metnin sonunda aramasını rica etmiş oluruz. Javascript de bizi kırmayıp, sonuçta görüldüğü üzere sadece metnin sonundaki gün kelimesini değiştirdi. Arada \n yeni satırlar da olmasına rağmen, tüm satırları atladı ve metnin en sonuna gitti. Eğer biz bu metnin çok satırlı(multiline) olduğunu belirtirsek sonuç değişir.

var aranan =  /gün$/m;
var metin = "sensiz geçen günleri ben GüNdEn saymıyorum düzgün\nBu günler yalancı bugün\nkötüye gidiyor günbegün\nve hergün\n";

var n = metin.replace(aranan, 'dün');

console.log(n);
/* sensiz geçen günleri ben GüNdEn saymıyorum düzdün
   Bu günler yalancı bugün
   kötüye gidiyor günbegün
   ve hergün*/

Sedece ilk satırın sonuna kadar gitti bu kez. Zîra biz ona global bir arama yapmasını söylemedik. Söyleyelim o zaman.

var aranan =  /gün$/mg;
var metin = "sensiz geçen günleri ben GüNdEn saymıyorum düzgün\nBu günler yalancı bugün\nkötüye gidiyor günbegün\nve hergün\n";

var n = metin.replace(aranan, 'dün');

console.log(n);
/* sensiz geçen günleri ben GüNdEn saymıyorum düzdün
   Bu günler yalancı budün
   kötüye gidiyor günbedün
   ve herdün*/

Bu kez tüm metni aradı ve her satırın sadece sonuna baktı. Multiline bu şekilde bir fayda sağlıyor işte. İleride daha da iyi anlayacağız tabi.

Konum Karakterleri
Karakter İşlevi
^ Düzenli ifade kalıbının, karakter dizisinin sadece başında aranacağını belirtir. Eğer m multiline anahtarı kullanılırsa her satırın sadece başında arama yapılır.
$ Düzenli ifade kalıbını karakter dizisinin sonunda arar. m multiline anahtarı belirtilirse her satırın sonunda arama yapılır
\b Aramanın kelime sınırında olacağını belirtir. Kelime sınırının başlangıcı bir karakterle başlar ve karakter olmayan(boşluk gibi) bir karakterle biter
\B Aramanın kelime sınırında olmayan yerlerde yapılacağını belirtir.

Şu kelime sınırı olayına hemen bakalım.

var aranan =  /\b/g;
var metin = "sen seni bil sen   seni";

var n = metin.replace(aranan, '|');

console.log(n);
/*|sen| |seni| |bil| |sen| |seni| */

Gayet açık değil mi? Kelime sınırı dediğimiz şey, kelimelerin başlangıç ve bitiş yerleri. Bu durumda kelime sınırı olmayan yerler de ortaya çıkmış oluyor sanırım.

var aranan =  /\B/g;
var metin = "sen seni bil sen seni";

var n = metin.replace(aranan, '|');

console.log(n);
/* s|e|n s|e|n|i b|i|l s|e|n s|e|n|i */
//console.log(String.fromString(metin));

Mantıklı bir sonuç değil mi? Bu kez kelimelerin başlangıç ve bitiş yerleri değil, bu başlangıç ve bitiş yerlerinin arasında kalan, kelime başlangıcı ve bitişi olmayan tüm yerler işleme tâbi tutuldu. Fakat bu kelime sınırı kosunda ingiliz alfabesi dışındaki dillerde büyük bir sorun var.

var aranan =  /\b/g;
var metin = "sensiz geçen günleri ben günden saymıyorum";

var n = metin.replace(aranan, '|');

console.log(n);
// |sensiz| |ge|ç|en| |g|ü|nleri| |ben| |g|ü|nden| |saym|ı|yorum|

Bu sorunun henüz müstakil bir çözümü bulunmamakta ama bu konuyu yiyip bitirdikten sonra ancak bu soruna çözüm üretebileceğimizi düşünmekteyim. O yüzden bu aşamada bu sorunla uğraşmak bize vakit kaybettirir. Şimdilik kelime sınırı konusunda bu şekil kullanımlardan kaçınmamız gerek. Veya bir çözüm önerin varsa lütfen face'den dürt beni.

Diğer bir konum karakteri olan ^ karakteri şu şekilde.

var aranan =  /^gün/g;
var metin = "günlerim sensiz geçiyor\ngünlerim ağlak";

var n = metin.replace(aranan, 'dün');

console.log(n);
/*dünlerim sensiz geçiyor
  günlerim ağlak*/

Karakter dizisinin sadace en başına baktı, diğer satıra bakmadı, çünkü çok satırlı olduğunu belirtmedik, o yüzden karakter dizisini bir bütün olarak ele aldı. Diğer satıra da bakması için tabiki m multiline anahtarını balirtmemiz gerek.

var aranan =  /^gün/gm;
var metin = "günlerim sensiz geçiyor\ngünlerim ağlak";

var n = metin.replace(aranan, 'dün');

console.log(n);
/*dünlerim sensiz geçiyor
  dünlerim ağlak*/

Böyle sürekli düzenleme üzerinden gidiyoruz ama düzenli ifadeler daha çok denetleme amacıyla kullanılır. Yani bir eşleşme aranır. Bunun için RegExp nesnesinin kendine has metotları olan exec() ve test() metotları kullanılır. test metodu, karakter dizisini test eder ve aradığımız eşleşme varsa true döndürür, yoksa false.

var aranan =  /gün/;
var metin = "günlerim sensiz geçiyor";

var n = aranan.test(metin);

console.log(n);  // true
var aranan =  /ay/;
var metin = "günlerim sensiz geçiyor";

var n = aranan.test(metin);

console.log(n);  // false

Bu metot RegExp nesnesinin metodu olduğu için oluşturduğumuz RegExp nesnesi üzerinden kullanıyoruz metodu. exec metodu ise şu şekilde.

var aranan =  /gün/;
var metin = "günlerim sensiz gün gibi geçiyor";

var n = aranan.exec(metin);

console.log(n);  // ["gün", index: 0, input: "günlerim sensiz gün gibi geçiyor"]

Bu metot bir dizi döndürüyor. Sonuca göre aradığımız şey karakter dizisinde var ve 0. index'ten başlıyor. Bu metotları kullandığımızda, RegExp nesnesine ait olan lastIndex özelliği de sonuca göre güncellenir. lastIndex özelliği, yukarıdaki arama sonucunda 0 değerini taşır. Fakat eğer global bir arama yaparsak bu lastIndex özelliği eşleşmeden hemen sonraki index'i gösterir.

var aranan =  /gün/g;
var metin = "günlerim sensiz gün gibi geçiyor";

var n = aranan.exec(metin);
console.log(aranan.lastIndex); // 3
console.log(n);               // ["gün", index: 0, input: "günlerim sensiz gün gibi geçiyor"]

Eğer eşleşme yoksa exec metodu null döndürür ve lastIndex ise 0 değerini alır. Eğer global arama yapmıyorsak, eşleşme olsa da olmasa da lastIndex daima 0 olur.

var aranan =  /ay/;
var metin = "günlerim sensiz gibi geçiyor";

var n = aranan.exec(metin);
console.log(aranan.lastIndex); // 0
console.log(n);                // null

Tekrar string metotlarına dönecek olursak match() metodu, eğer global arama yapmıyorsak exec() metodu gibi bir sonuç döndürür.

var aranan =  /gün/;
var metin = "günlerim sensiz gün gibi geçiyor gün";

var n = metin.match(aranan);

console.log(n); // ["gün", index: 0, input: "günlerim sensiz gün gibi geçiyor gün"]

Global arama yapıyorsak ise şöyle:

var aranan =  /gün/g;
var metin = "günlerim sensiz geçiyor";

var n = metin.match(aranan);

console.log(n); // ["gün"]
var aranan =  /gün/g;
var metin = "günlerim sensiz gün gibi geçiyor gün";

var n = metin.match(aranan);
console.log(aranan.lastIndex); // 0
console.log(n); // ["gün", "gün", "gün"]

Eğer eşleşme olmazsa null döndürür.

var aranan =  /ay/g;
var metin = "günlerim sensiz gün gibi geçiyor gün";

var n = metin.match(aranan);

console.log(n); // null

String metotlarının bir diğer ise search metodu idi. Bu metot ilk bulduğu eşleşmenin karakter dizisi içindeki index'ni verir, yoksa -1 döndürür.

var aranan =  /gün/;
var metin = "günlerim sensiz gün gibi geçiyor gün";

var n = metin.search(aranan);

console.log(n); // 0

Global arama yapsak da sonuç değişmez, bulduğu ilk eşleşmenin index'ini verir.

var aranan =  /gün/g;
var metin = "Günlerim sensiz gün gibi geçiyor gün";

var n = metin.search(aranan);
console.log(n); // 16

Evet yavaş yavaş giriyoruz düzenli ifadelere. Evet yanlış okumadın, henüz düzenli ifadelere girmedik, sadece kapıdan el salladık. Buraya kadar cephanemizi, mataramızdaki suyu yokladık ve eksiklerimizi bir parça giderdik. Artık düzenli ifadelere girmemizin vakti geldi çattı.

Neden düzenli ifadeler?

Bu soruyu cevaplandırmayacağız bu başlık altında. E niye böyle bir başlık attın o zaman? Çeşitli konulardan bahseden intenet eşrâfı kendine böyle bir âdet edinmiş, bahsettiği konu ile ilgili mutlaka neden falan, neden filan diye başlıklar atıp altında da ileri geri yazıp dururlar. Ben de hep özenmişimdir bu neden filan olayına. O yüzden böyle bir başlık attım. Kendimi çok iyi hissediyorum. Pişman değilim. Bu düzenli ifadelerin amacı konusunda yeteri kadar altı çizili laflar ettim mi bilmiyorum, çünkü çoğu kişi, çoğu yaygın kanı düzenli ifadelerden amacının üstünde şeyler bekler. Veya konuya öyle bir önyargı ile girer. Bu önyargı, bu yanlış görüş, bu çarpık düşünce, düzenli ifadelerin arama yapma konusunda üstün olduğu ve bu yönde kullanıldığı. Evet arama da yapıyoruz düzenli ifadelerle ama ana amaç bu değil ve şöyle biraz düşünürsen arama konusunda düzenli ifadelerin zayıf olduğunu kolayca anlarsın. Yukarıdan beri yaptığımız örneklerde bu belli zaten. Mesela arama yaptık diyelim search metoduyla, veya eşleşme aradık match metoduyla veya diğerleriyle. Hiçbiri de bulduğu tüm eşleşmeleri metnin tamamında nerde bulduğunu bildirmiyor. Sadece ilk bulduğunu bildiriyor. Mesela bir metnin içinde geçen tüm filan kelimelerinin yerlerini tek tek tespit etmek istersek extra bir döngü kurmamız gerekir. Eğer böyle bir arama için döngü kuracaksam şahsen düzenli ifadeleri kullanmam. String metotlarını normal şekilde kullanmak bu durumda daha hızlı ve daha verimlidir. String metotlarının normal kullanımı düzenli ifadelerden her zaman daha hızlı çalışır. Ve programlama câmiyasında hız ve verimlilik dini bir vecibe gibidir. Harikalar da yaratsan yazdığın kodlarla, hızlı ve verimli değilse at çöpe. Eğer amacımız saf arama yapmaksa düzenli ifadelerle işimiz yok. Gerçi bu biraz da javascript'ten kaynaklanıyor, çünkü diğer programlama dillerinde düzenli ifadeler çok daha geniş kapsamlı ve çok daha çetrefilli. Bunun en büyük sebebi javascript'i bugüne kadar kisenin adam yerine koymaması. Ama bugün javascript'in girmediği delik kalmadı. Herşeye rağmen düzenli ifadelerin geneline baktığında bir kontrol sözkonusudur. Ana amaç kontroldür yani. Düzenli ifadelerle kullanılan en etkin metot replace metodudur değişiklik üzerine. Diğerleri saf kontrol. Bu konunun altını çizdiğimize göre yolumuza devam edebiliriz.

Karakter Sınıfları
Karakter Sınıfı İşlevi
\d 0-9 arası herhangi bir sayısal karakterin yerine geçer
\D Sayısal olmayan herhangi bir karakterin yerine geçer
\w A-Z, a-z, 0-9, _, arasında herhangi birinin yerine geçer. Bunların hepsine birden kelime karakterleri deniliyor.
\W Kelime karakteri olmayan herhangi bir karakterin yerine geçer.
\s Boşuk, tab gibi whitespace denen karakterlerinin yerine geçer.
\S whitespace olmayan herhangi bir karakterin yerine geçer.
. Yeni satır karakteri \n hariç herhangi bir karakterin yerine geçer.
[...] Parantezin içine yazılacak karakterlerden herhangi birinin yerine geçer. Mesela [abc], bu karakterlerin herhangi birinin yerini tutar.
[^...] Parantezin içine yazılacak karakterlerin dışında herhangi bir karakterin yerine geçer. Mesela [^abc], bu karakterler hariç anlamına gelir.

Hepsini tek tek inceleyeceğiz ama istersen nicelik belirten özel karakterlerin de tablosunu görelim ve iki tabloyu içiçe birlikte inceleyelim

Özel karakterler
Özel karakter Anlamı
{n} Kendinden önce gelen karakterin n kez tekrar edeceğini belirtir.
{n,} Kendinden önce gelen karakterin n kez yada daha fazla tekrar edeceğini belirtir.
{n,m} Kendinden önce gelen karakterin en az n en çok m kez tekrar edeceğini belirtir.
? Kendinde önce gelen karakterin 0 yada 1 kez tekrar edeceğini belirtir.
+ Kendinde önce gelen karakterin 1 yada daha fazla kez tekrar edeceğini belirtir.
* Kendinde önce gelen karakterin 0 yada daha fazla kez tekrar edeceğini belirtir.

\d

Anlamının gayet açık olduğunu düşünüyorum.

var metin = ['12a45fyt', '343ed45', '36h6u8iu', 'edfg4098', '34gh89ol', 'erf456hy', '98hkyit5'];

Elimizde böyle bir dizi var ve biz sayıyla başlayıp sayıyla biten elemanları ayıklamak istiyoruz. Hem yukarıdaki tablolara bakalım hem de bağıra bağıra düzenli ifademizi kurmaya başlayalım.

/\d       \d/

Bu kadarını hepimiz anladık, zaten bu başlık altındayız. Peki aradaki boşluğa ne gelecek? Bana bakma, yukarıdaki tabloya bak. Tablodaki . (nokta) karakteri senin de dikkatini çekmiş olmalı. Evet aradığımız şey o. İster sayı olsun ister harf olsun ister başka birşey olsun farketmez, yeterki bir şey olsun anlamına geliyor bu nokta. Fakat araya kaç tane . (nokta) koyacağımız kesin değil, çünkü elemanların karakter sayıları birbirinden farklı. Demekki neymiş, bize bir yardımcı oyuncu daha lazımmış. Bu kriterimiz ise en az bir karakter olması, sayı veya harf farketmez, en az bir tane olduktan sonra gerisi önemli değil. Bu kritere uyan + karakterini görüyoruz tabloda değil mi? Bunu nokta karakterinin ardına koyursak bu iş olur gibi. Yani

var aranan =  /\d.+\d/;

böyle oldu anlattıklarımıza göre. Bir sayı ile başlıyor \d, sonra en az bir ve daha fazla tekrar eden herhangi bir karakter .+ ve sonra yine sayı ile bitiyor \d. Şimdi test edelim.

var aranan =  /\d.+\d/;
var metin = ['12a45fyt', '343ed45', '36h6u8iu', 'edfg4098', '34gh89ol', 'erf456hy', '98hkyit5'];

    for(var x = 0; x < metin.length; x++){
        
        if(aranan.test(metin[x])){
            
            console.log(metin[x]);
            
        }
    }
    /*12a45fyt
      343ed45
      36h6u8iu
      edfg4098
      34gh89ol
      erf456hy
      98hkyit5*/

Sonuç tam bir fiyasko. Peki neden böyle oldu? İstediğimiz ölçü sayı ile başlayıp sayı ile bitmesiydi ama elemanların hepsi geldi. Halbuki dizide sayıyla başlayıp sayıyla biten sadece iki eleman var. Biz /\d.+\d/ bu tanımlamayla, sayı ile başlayıp sayı ile bitmesini belirtecek bir işaret koymadık aslında. Yani bu ölçümüz elemanın herhangi bir yerinde geçse de uygun olacak şekilde. Peki başlangıç ve bitişi nasıl belirtiyorduk? \b karakteriyle değil mi? Yani kelime sınırı. Hemen deneyelim.

var aranan =  /\b\d.+\d\b/;
var metin = ['12a45fyt', '343ed45', '36h6u8iu', 'edfg4098', '34gh89ol', 'erf456hy', '98hkyit5'];

    for(var x = 0; x < metin.length; x++){
        
        if(aranan.test(metin[x])){
            
            console.log(metin[x]);
            
        }
    }
/*343ed45
  98hkyit5*/

Evet bu kez amacımıza ulaştık. Ama yukarıda bir yerlerde bu kelime sınırı karakterinin türkçe harflere karşı alerjisini görmüştük. Listemizdeki elemanların içinde türkçe harfler olsaydı yine istemediğimiz sonuçlarla karşılaşırdık.

var aranan =  /\b\d.+\d\b/;
var metin = ['12a4ğ5fyt', '343edı45', '36h6u8iu', 'edfgç4098', '34gh89ol', 'erf456hy', '98hkyit5'];

    for(var x = 0; x < metin.length; x++){
        
        if(aranan.test(metin[x])){
            
            console.log(metin[x]);
            
        }
    }
/*12a4ğ5fyt
  343edı45
  edfgç4098
  98hkyit5*/

Evet istenmeyen elemanlar da geldi. Peki bu durumda ne yapacağız? Elimiz kolumuz bağlı mı oturacağız? Tabiki hayır. Yukarıdan beri öğrendiğimiz bir iki kungfu tekniğiyle bu sorunu halledeceğiz. Ölçümüz neydi? Sayı ile başlayıp sayı ile bitsin. Yani elemanın hem başında hem sonunda sayı olsun. Peki biz bunu başka bir düzenli ifade ile ifade edemez miyiz?

var aranan =  /^\d.+\d$/;
var metin = ['12a4ğ5fyt', '343edı45', '36h6u8iu', 'edfgç4098', '34gh89ol', 'erf456hy', '98hkyit5'];

    for(var x = 0; x < metin.length; x++){
        
        if(aranan.test(metin[x])){
            
            console.log(metin[x]);
            
        }
    }
/*343edı45
  98hkyit5*/

Düzenli ifademiz /^\d.+\d$/ böyle oldu ve sorunumuz çözüldü. ^ karakteri aradığımız ölçünün elemanın başında olduğunu, $ karakteri ise sonunda olduğunu belirtiyor. Fakat bu çözüm sadece bu sorunu ve buna benzer sorunları çözer. Yani kelime sınırı karakteri ile ilgili hâlâ ciddi sorunlarımız olduğunu aklından çıkarma. Ayrıca bu neticeden şunu çıkarıyoruz, bir düzenli ifade kalıbı, birden çok şekilde ifade edilebilir. Yani o anki duruma uygun aklımıza gelen en basit ifadeyi kullanırız.

var metin = [123', '343edı45', 456, 'hebele', '789', 'yağmur', '98hkyit5'];

Listemiz böyle olsaydı ve biz sadece sayı içeren elemanları ayıklamak isteseydik.

var aranan =  /\d+/;
var metin = [123, '343edı45', 456, 'hebele', '789', 'yağmur', '98hkyit5'];

    for(var x = 0; x < metin.length; x++){
        
        if(aranan.test(metin[x])){
            
            console.log(metin[x]);
            
        }
    }
/*123
  343edı45
  456
  789
  98hkyit5*/

Bu ilk denememiz de yine fiyasko ile sonuçlandı. Çünkü yine aynı hatayı yaptık. /\d+/ bu tanımlama ile en az bir ve daha fazla sayı karakteri elemanın içinde de olsa ver demiş olduk. Aslında bu eşleşmenin tam olarak nasıl olduğunu match metodu ile görebiliriz. match metodu eşleşme olursa bize bir dizi döndürüyordu değil mi? Ve bu dizinin ilk ve tek elemanı yapılan eşleşmeyi tutuyordu. Ve ayrıca dizinin bir de input diye bir özelliği vardı, bu da eşleşmenin yapıldığı kaynak string'i tutuyordu. Biz bu iki bilgiye bakarak hangi elemanların nasıl eşleştiğini görebiliriz. Ama match metodu karakter dizilerinin metodu olduğu olduğu için listedeki gerçek sayıları da string'e çevirmemiz gerek değil mi?

var aranan =  /\d+/;
var metin = [123, '343edı45', 456, 'hebele', '789', 'yağmur', '98hkyit5'];

    for(var x = 0; x < metin.length; x++){
        
        var y;
        
        if( y = metin[x].toString().match(aranan)){
            
            console.log(y[0], y.input);
            
        }
    }
/*123 123
  343 343edı45
  456 456
  789 789
  98  98hkyit5*/

Evet, hangi eşleşmenin hangi elemanla olduğu gayet açık ve hangi mantıkla bize sayı olmayan elemanları verdiğini gayet net bir şekilde görüyoruz. Aradığımız ölçü elemanların içinde de geçiyor. Biz kelime sınırını belirtmediğimiz için sonuç böyle oldu.

var aranan =  /\b\d+\b/;
var metin = [123, '343ed45', 456, 'hebele', '789', 'yağmur', '98hkyit5'];

    for(var x = 0; x < metin.length; x++){
        
        var y;
        
        if( y = metin[x].toString().match(aranan)){
            
            console.log(y[0], y.input);
            
        }
    }
/*123 123
  456 456
  789 789*/

Bu kez eşleşmeleri net bir şekilde aldık. Kullandığımız karakterlere dikkat et. \b kelime sınırı ile başlıyor, \d+ en az bir veya daha fazla sayısal karakterle devam ediyor ve \b kelime sınırı ile bitiyor. Aradığımız elemanlar listede aslında üç basamaklı üç sayı. Yani bunu /\d\d\d/ şeklinde de belirtebilirdik. Gerçi baştaki eleman da gelirdi ama, demek istediğim \d+ ifadesi ile biz bir veya daha fazla basamaklı sayıyı temsil ediyoruz. Yani burada aslında bir nevi üstü kapalı bir konuşma var. Ve üstü kapalı bu konuşma bir genelleme oluşturuyor ve bir çok elemanı aynı anda kastetmiş oluyoruz. Düzenli ifadelerin yaptığı şey tamamen budur işte.

var aranan =  /\b\D+\b/;
var metin = [123, '343ed45', 456, 'hebele', '789', 'yağmur', '98hkyit5'];

    for(var x = 0; x < metin.length; x++){
        
        var y;
        
        if( y = metin[x].toString().match(aranan)){
            
            console.log(y[0], y.input);
            
        }
    }
/*hebele hebele
  yağmur  yağmur*/

Bu kez elemanın sayı içermemesini istedik \D karakteri ile.

\w

Bu karakter sınıfı ile, elle tutulup gözle görülen, bir ağırlığı ve hacmi olup uzayda yer kaplayan tüm karakterleri temsil edebiliriz. Buna bazı noktalama karakterleri dahil değil ama _ alt tire dahil. Gavurların whitespace dediği, gözle görülüp elle tutulmayan, uzayda yer kaplamayan, boşluk tarzı karakterler bu grubun dışında kalıyor. Dışarda kalan bu grubu ise \W karakter sınıfı üstleniyor.

var metin = 'gelecek ay 24 mart çarşamba. hava güneşli';

Bu yazıdan sadece 24 mart ifadesini ayıklamak istiyoruz. İlk göze çarpan ölçümüz yanyana iki sayı karakteri. Bunu \d{2} bu şekilde karşılayabiliriz değil mi? Sayımız tamı tamına 2 kez tekrar edecek. Sonra arada bir boşluk var. Bunu da \s karakteri karşılar değil mi? Son olarak da \w karakteri ile kalanları temsil edebiliriz. Ve kelimenin sonunda da bitmeli. Yani \b.

var aranan =  /\d{2}\s\w+\b/;
var metin = 'gelecek ay 24 mart çarşamba. hava güneşli';

var n = metin.match(aranan);

console.log(n[0]); //24 mart

Bu kez bir hamlede almayı başardık. Ama bunu kutlamadan önce sana kötü bir haber vermek istiyorum. \w karakter sınıfının da türkçe karakterlere alerjisi var.

var aranan =  /\d{2}\s\w+\b/;
var metin = 'bugün 24 şubat çarşamba. hava güneşli';

var n = metin.match(aranan);

console.log(n); // null

null sonucunu yüzümüze bir tokat gibi yedik. Halbuki biz adımız gibi biliyoruzki orada alfanumerik bir karakter var. Anladık bizi sevmiyorsunuz da, alfabemizle ne sorununuz var be kardeşim. Açıkçası bu gibi sorunlar çok canımı sıkıyor. Bütün keyfim kaçıyor bir anda. Bu sorunu da ifademizi değiştirerek çözeceğiz.

var aranan =  /\d{2}\s.+\b/;
var metin = 'bugün 24 şubat çarşamba. hava güneşli';

var n = metin.match(aranan);

console.log(n[0]); // 24 şubat çarşamba. hava güneşli

Bu şekilde istemediğimiz yerler de geldi. O yüzden daha net bir tanım yapmamız gerekiyor. Ama yukarıda bahsettiğim gibi, düzenli ifadelerin gücü muammadan kaynaklanıyor. Yani sen düzenli ifadeni netleştirdikçe kapsam alanın daralıyor. Bu noktada karakter dizisini de iyi analiz etmek gerek. Bilmediğimiz bir dizide net tanımlarla işlem yapamayız. Ama burada ne olduğu bariz belli.

var aranan =  /\d{2}\s.{5}\b/;
var metin = 'bugün 24 şubat çarşamba. hava güneşli';

var n = metin.match(aranan);

console.log(n[0]); // 24 şubat

Burada karakter dizisini tanımak bize avantaj sağladı ama bunun her zaman böyle olmayacağı ortada bir gerçek. Javascript bu konularda çok eksik. Ama biz onu eksiklerine rağmen seviyoruz değil mi? İşte gerçek sevgi budur.

var metin = 'benim.,= numaram ^+haftaya...,,. hava,,...***?? sisli';

Burada noktalama işaretleri haricindeki yazıyı ayıklamak istiyoruz. Hariç tutmak için yukarıdaki tabloda [^ ] özel karakterini görüyoruz. Hemen deneyip bakalım.

var aranan =  /[^\w]/g;
var metin = 'benim.,=& numaram ^+haftaya...,,. hava,,...***?? sisli';

var n = metin.replace(aranan, '');

console.log(n); // benimnumaramhaftayahavasisli

Diğer karakterleri eledik ama sonuç çok da istediğimiz gibi değil. Bunun daha anlamlı olması için aralarında en azından bir baoşluk olsa fena olmazdı değil mi?

var aranan =  /[^\w\s]/g;
var metin = 'benim.,=& numaram ^+haftaya...,,. hava,,...***?? sisli';

var n = metin.replace(aranan, '');

console.log(n); // benim numaram haftaya hava sisli

Bu şekilde boşlukları da hariç tutmuş olduk. Ayrıca global bir arama yaptığımıza da dikkat etmişsindir sanırım. Yoksa sadece ilk eşleşmede olay bitecekti değil mi? Kısacası \w karakteri tüm alfanumerik karakterlerin yerine geçiyor. Sanırım olayın nasıl yürüdüğünü anladık. Biraz daha örnek yapıp konuyu kapatalım.

var aranan =  /^at|at$/g;
var metin = ['at', 'kat', 'yat', 'sat', 'yatırım', 'satış', 'batak', 'yatak', 'rahat', 'kabahat', 'atak'];

for(let x = 0; x < metin.length; x++){
    
    if(metin[x].match(aranan)){
        console.log(metin[x]);
    }
}
/*at
  kat
  yat
  sat
  rahat
  kabahat
  atak*/

Burada yeni bir özel karakter kullandık. | karakteri hem at ile başlayan hem at ile biten elemanları almamızı sağladı. Bir nevi veya anlamına geliyor. Eğer bunu kullanmazsak:

var aranan =  /^at$/;
var metin = ['at', 'kat', 'yat', 'sat', 'yatırım', 'satış', 'batak', 'yatak', 'rahat', 'kabahat', 'atak'];

for(let x = 0; x < metin.length; x++){
    
    if(metin[x].match(aranan)){
        console.log(metin[x]);
    }
}
/* at */

Hem at ile başlayıp hem at ile biten elemanı verdi. Ama bunda bir nevi ve anlamı var. O yüzden her iki özelliği aynı anda taşıyan elemanı verdi.

Bu arada önemli bir şey unutmuşuz ya. split() fonksiyonunu.

split()

Bu metot da karakter dizilerinin metotlarından biri ve buna da argüman olarak düzenli ifade kalıbı verebiliriz. Daha önce bahsetmemiz gerekirdi ama unutmuşum. Sen de hiç hatırlatmıyorsun ama. Ne kadar ayıp. Bu fonksiyon gayet kullanışlı ve çok işimize yarayacak bir fonksiyon. Bir karakter dizisini belirli bir yerden bölmek için kullanılır. Karakter dizisini böler ve bu bölünen parçaları bir dizi halinde verir.

var aranan =  /\/\//;
var metin = 'http://google.com';

var n = metin.split(aranan);
console.log(n);
/* ["http:", "google.com"] */

Düzenli ifade kalıbımız biraz garip görünüyor haklısın. Bunun sebebi özel anlama gelen karakterleri kullanırken, bu özel anlamını değil de normal bir karakter anlamını, yani kendini temsil etsin diye önüne \ ters bölü işareti koymamız. Biz burada yanyana iki bölü // işaretini yakalamak istediğimiz için her birinin önüne ters bölü \ koyduk. Bu, tüm özel anlam ifade eden karakterler için geçerli. Mesela $ karakteri düzenli ifade içinde kullanıldığında karakter dizisinin sonuna bakılacağını belirtiyor ama peki biz gerçekten de bu dolar işaretini arıyorsak?

var aranan =  /\d\d$/;
var metin = 'http://google.com?20$';

var n = metin.match(aranan);
console.log(n);
/* null */

Gördüğün gibi javascript bizim yazının sonuna bakmak istediğimizi düşünüyor. Biraz yardıma ihtiyacı var.

var aranan =  /\d\d\$/;
var metin = 'http://google.com?20$';

var n = metin.match(aranan);
console.log(n);
/* ["20$", index: 18, input: "http://google.com?20$"] */

Bir html elemetini html kodlarına çevirmeyi deneyelim. Çevireceğimiz element yukardaki son başlık olsun. Yani şu: <h3>split()</h3>. Yani sayfada ben yazarken böyle görünüyor, ama senin bu şekilde görmen için bunu html kodlarına çeviriyorum. Bu işi bir de biz deneyelim.

var elemanlar = ['<', '>', '\/', '\\(', '\\)'];

Bunlar çevireceğimiz elemanlar. Parantezler için iki ters bölü kullandık özel anlamlarından dolayı. Çünkü parantezler de düzenli ifade içinde özel anlam taşıyor. Birazdan bununla ilgili örnek yapalım. Peki bunları neye çevireceğiz?

var karşılıklar = ['&lt;', '&gt;', '&sol;', '&lpar;', '&rpar;'];

İşte bunlar da html kod karşılıkları. Hatta bunu bir tablo olarak görsek sanki daha iyi olacak gibi.

Karakter html kodu
< &lt;
> &gt;
/ &sol;
( &lpar;
) &rpar;

Şimdi çevirebiliriz.

var elemanlar = ['<', '>', '\/', '\\(', '\\)'];
var karşılıklar = ['&lt;', '&gt;', '&sol;', '&lpar;', '&rpar;'];
var metin = '<h3>split()</h3>';
    
for(let x = 0; x < elemanlar.length; x++){

    let aranan = new RegExp(elemanlar[x], 'g');
    metin = metin.replace(aranan, karşılıklar[x]);
    
}

console.log(metin); // &lt;h3&gt;split&lpar;&rpar;&lt;&sol;h3&gt;

Döngünün her turunda elemanlar dizisinin bir elemanıyla düzenli ifade kuruyoruz ve yine aynı sıradaki karşılığını takas ediyoruz. İki diziyi bu yüzden birbirine paralel yaptık. Ve her turda metin değişkeni dönen yeni değerle yenileniyor ve sonunda tamamen kodlanmış hale geliyor.

gruplama

Düzenli ifade içinde bazı karakterleri () parantez içine alarak gruplayabiliriz.

var metin = 'bu araba harika. ama şurada çok daha güzel arabalar var.';
var aranan = /araba(lar)?/g;

var n = metin.replace(aranan, 'kamyon');
console.log(n); // bu kamyon harika. ama şurada çok daha güzel kamyon var.

Parantez içinde aldığımız (lar) ifadesine toplu şekilde ? özel karakterini uyguladık. Yani lar kelimesi hiç geçmese de olur, bir kere geçse de olur demiş olduk.

var metin = 'kamyonculuk kamyon sürmekle olur. kamyonsuz kamyoncu mu olurmuş.';
var aranan = /kamyon(cu|suz)/g;

var n = metin.match(aranan);
console.log(n); // ["kamyoncu", "kamyonsuz", "kamyoncu"]

Parantez olayının bazı ilginç özellikleri de var.

var metin = 'benim adım ebruli';
var aranan = /(benim)\s(adım)\s(ebruli)/;

var n = metin.replace(aranan, '$3 $1 $2');
console.log(n); // ebruli benim adım

Parantez içine aldığımız elemanlar soldan sağa doğru birinci parantez $1, ikici $2 üçüncü $3 falan diye gidiyor.

x(?=y)

x ancak arkasından y geliyorsa eşleşir.

var metin = 'kamyonu kamyoncu sürer. kamyonculuk böyle olur';
var aranan = /kamyon(?=cu)/g;

var n = metin.match(aranan);
console.log(n); // ["kamyon", "kamyon"]

Burada eşleşmeyen en baştaki kamyonu kelimesi. Çünkü arkasından cu ifadesi gelmiyor. Bunu şöyle daha iyi görebiliriz.

var metin = 'kamyonu kamyoncu sürer. kamyonculuk böyle olur';
var aranan = /kamyon(?=cu)/;

metin = metin.split(' ');
    
for(let x = 0; x < metin.length; x++){
    
    var eşleşme = metin[x].match(aranan);
    
    if(eşleşme){
        console.log(eşleşme[0], eşleşme.input);
    }
}
/* kamyon kamyoncu
   kamyon kamyonculuk */

x(?!y)

x ancak arkasından y gelmiyorsa eşleşir.

var metin = 'kamyonu kamyoncu sürer. kamyonculuk böyle olur';
var aranan = /kamyon(?!cu)/;

metin = metin.split(' ');
    
for(let x = 0; x < metin.length; x++){
    
    var eşleşme = metin[x].match(aranan);
    
    if(eşleşme){
        console.log(eşleşme[0], eşleşme.input);
    }
}
/* kamyon kamyonu */

Yukarıda menzil dışında kalan şimdi içerde. Çünkü kamyonu kelimesinde, kamyon kısmından sonra cu ifadesi yok.

var metin = ['esra', 'esma', 'esmehan', 'ismihan', 'esmelan', 'esmanur', 'esmer', 'esragül'];
var aranan = /(es[mr][ae])(?=.)/;

for(let x = 0; x < metin.length; x++){
    
    let eşleşme = metin[x].match(aranan);
    
    if(eşleşme){
        
        console.log(eşleşme[0], eşleşme.input);
    }
}
/* esme esmehan
   esme esmelan
   esma esmanur
   esme esmer
   esra esragül*/

Burada ise esma esra isimlerini yalın olarak verme demiş olduk, devamında mutlaka bir ek olmalı. Bunun tersini yaparsak:

var metin = ['esra', 'esma', 'esmehan', 'ismihan', 'esmelan', 'esmanur', 'esmer', 'esragül'];
var aranan = /(es[mr][ae])(?!.)/;

for(let x = 0; x < metin.length; x++){
    
    let eşleşme = metin[x].match(aranan);
    
    if(eşleşme){
        
        console.log(eşleşme[0], eşleşme.input);
    }
}
/* esra esra
   esma esma*/

Böyle yalın şekilde almış oluruz.

var metin = ['esra', 'esma', 'esmehan', 'ismihan', 'esmelan', 'esmanur', 'esmer', 'esragül'];
var aranan = /es[mr]a/;

for(let x = 0; x < metin.length; x++){
    
    let eşleşme = metin[x].match(aranan);
    
    if(eşleşme){
        
        console.log(eşleşme[0], eşleşme.input);
    }
}
/* esra esra
   esma esma
   esma esmanur
   esra esragül */ 
var metin = ['257654', '4537', '87789', '123', '32212', '0987', '32221', '3890'];
var aranan = /[1-3]{3}/;

for(let x = 0; x < metin.length; x++){
    
    let eşleşme = metin[x].match(aranan);
    
    if(eşleşme){
        
        console.log(eşleşme[0], eşleşme.input);
    }
}
/* 123 123
   322 32212
   322 32221 */ 

Burada ise 1 ile 3 arasında yanyana 3 sayı içeren elemanları arıyoruz.

var metin = ['257654', '4537', '87789', '123', '32212', '0987', '32221', '3890'];
var aranan = /[3-7]{3}/;

for(let x = 0; x < metin.length; x++){
    
    let eşleşme = metin[x].match(aranan);
    
    if(eşleşme){
        
        console.log(eşleşme[0], eşleşme.input);
    }
}
/* 576 257654
   453 4537 */

3 ile 7 arasında yanyana 3 sayı arıyoruz.

console.log(
    
    'beni sana hapsettin'
    .replace( /[aeouoöüıi]/g, '' )
    
); // bn sn hpsttn

Bu kez sesli harfleri eledik.

14 Ocak 2016 Perşembe

CSS3 Gradients(renk geçişleri)

      CSS3 renk geçişleri oluşturabilmek için iki fonksiyon tanımlamıştır.

  • linear-gradient
  • radial-gradient

      İsimlerinden de anlaşılacağı üzere biri doğrusal(linear), diğeri dairesel(radial) geçişler olurturuyor. Bu özellik ancak, özellik olarak resim atayabildiğimiz elementlerde kullanılabilir. Mesela background-image, border-image, list-style-image gibi elementler. Biz daha çok background-image üzerinden gideceğiz ve filmin sonunda border-image durumuna bir göz atacağız. Şöyle bir elementimiz olsun:

<div class="boxx">Lorem ipsum</div>

      CSS:

.boxx { background-image: linear-gradient(yellow, green); }
Lorem ipsum

       Fonksiyonun en basit ve sade kullanımı yukarıdaki kullanımdır. Fonksiyona yazdığımız birinci renkten ikinci renge doğru bir geçiş olmakta. Ve bu şekilde başka bir değer belirtmezsek işlem, yukarıdan aşağı doğru gerçekleşmekte. Bu yönü değiştirmek için derece kullanabildiğimiz gibi anahtar kelimeler de kullanabiliyoruz. Mesela:

      to left

.boxx { background-image:linear-gradient(to left, yellow, green) }
Lorem ipsum

      to right

.boxx { background-image:linear-gradient(to right, yellow, green) }
Lorem ipsum

      to left top

.boxx { background-image:linear-gradient(to left top, yellow, green) }
Lorem ipsum

      to right bottom

.boxx { background-image:linear-gradient(to right bottom, yellow, green) }
Lorem ipsum

      10deg

.boxx { background-image:linear-gradient( 10deg, yellow, green) }
Lorem ipsum

      20deg

.boxx { background-image:linear-gradient( 20deg, yellow, green) }
Lorem ipsum

      30deg

.boxx { background-image:linear-gradient( 30deg, yellow, green) }
Lorem ipsum

      40deg

.boxx { background-image:linear-gradient( 40deg, yellow, green) }
Lorem ipsum

      50deg

.boxx { background-image:linear-gradient( 50deg, yellow, green) }
Lorem ipsum

      60deg

.boxx { background-image:linear-gradient( 60deg, yellow, green) }
Lorem ipsum

      70deg

.boxx { background-image:linear-gradient( 70deg, yellow, green) }
Lorem ipsum

      80deg

.boxx { background-image:linear-gradient( 80deg, yellow, green) }
Lorem ipsum

      90deg

.boxx { background-image:linear-gradient( 90deg, yellow, green) }
Lorem ipsum

      120deg

.boxx { background-image:linear-gradient( 120deg, yellow, green) }
Lorem ipsum

      Derece değerlerinin hangi yönde ilerlediğini anladığını umuyorum. Sıfır derecede aşağıdan yukarıya işlem yapıyor. Derede arttıkça açı saat yönünde ilerliyor. 90 derece soldan sağa, 180 derece yukarıdan aşağıya doğru işlem yapıyor. Yön belitmediğimiz zaman da yukarıdan aşağıya doğru işlem yapıyordu değil mi, demekki varsayılan değer 180deg imiş.
W3Schools bu fonksiyonu şöyle bildiriyor bize:

linear-gradient(direction, color-stop1, color-stop2, ...)

Bu tanım en az iki renk bildirmek zorunda olduğumuzu gösteriyor. Sondaki üç nokta ise (en az iki renk belirledikten sonra) istediğimiz kadar renk belirtebileceğimizi gösteriyor. Bakalım gerçekten öylemi?

.boxx { background-image:linear-gradient( 90deg, yellow, green, blue, red, darkcyan, darkorange, fuchsia, lavender, lightcoral, powderblue, springgreen, saddlebrown, slateblue) }
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

      Evet çingene bohçası gibi bir elementimiz oldu. Belirlediğimiz renkler varsayılan olarak eşit bir şekilde alanı paylaşıyorlar. Kimin ne kadar alana yayılacağını biz de belirleyebiliriz. Tabi bu kadar çok renk üzerinde değil, biz yine iki renge dönelim. İki rengi, başka bir bilgi belirtmeden direk kullandığımız zaman birinci rengin değeri 0%, ikinci rengin değeri 100% olmuş oluyor. Yani geçiş, elementin tepesinden başlıyor alt sınırda bitiyor. Yani elementin görünen tüm alanı geçiş alanı olmuş oluyor. Nasıl oluyor? Tanıma tekrar bakalım.

linear-gradient(direction, color-stop1, color-stop2, ...)

      Bu tanımda direction'ın ne olduğu belli. Adamların color-stop dediği şeye ise biz kısaca renk durma noktası diyeceğiz. Evet çok da kısa değil ama mânâyı kaybetmemek için daha fazla kısaltmıyorum. Renk durma noktası işin tam tanımını yapıyor aslında. Zaten o yüzden bu adı vermişler buna. Yoksa niye versinler, manyak mı bu adamlar? Yapılan işlem durma noktası belirlemek renge.

.boxx { background-image: linear-gradient( yellow, green }
Lorem ipsum

      Bu durumda birinci rengin durma noktası 0%'dır. Buna direk 0 da diyebiliriz. Yani elementin üst sınır çizgisi. İkinci rengin durma noktası 100%'dür. Yani elementin alt sınır çizgisi. Arada kalan alan ise geçiş alanı. Bu iki durma noktası birbirinden ne kadar uzak olursa renklerin birbirine karışması da o kadar belli belirsiz olur. Bu durma noktalarına negatif değerler de verilebiliyor.

.boxx { background-image: linear-gradient( yellow -100%, red 200%) }
Lorem ipsum

      Dediğimiz gibi çok hafif bir geçiş oldu. Ve bunun tersi olarak, durma noktalarının arasındaki mesafeyi yakın tutarsak eğer daha düz bir geçiş elde ederiz.

.boxx { background-image: linear-gradient( yellow 40%, red 60%) }
Lorem ipsum

Peki aradaki mesafeyi sıfıra indirirsek ne olur?
Tabiki geçiş olmaz. Yani iki düz renk elde ederiz.

.boxx { background-image: linear-gradient( yellow 40%, red 40%) }
Lorem ipsum

      İki rengin de durma noktası aynı ama üçüncü bir renk olmadığı için elementin 40%'tan sonraki kısmını kırmızı renk kaplıyor. Üçüncü bir renk olsaydı o renk kaplayacaktı kalan alanı. Durma noktalarına aynı değerleri vererek şeritli arkaplanlar yapabiliriz. Bunun için sadece background-size özelliğine istediğimiz genişliğini vermemiz yeterli. background-size özelliği iki değer alır. Bu değerler sırasıyla x ve y ekseninde arkaplanın genişliğini belirlememizi sağlar. Arkaplan varsayılan olarak x ve y ekseninde elementi kaplayana kadar tekrar ettiği için sonuç şeritli bir arkaplan olur.

.boxx { background-image: linear-gradient( yellow 40%, red 40%); background-size:10px 30px }
Lorem ipsum

      Aslında burada 10x30'luk bir parça yaptık sadece. Eğer arkaplan tekrar etmesin dersek bu parçayı görebiliriz.

.boxx { background-image: linear-gradient( yellow 40%, red 40%); background-size:10px 30px; background-repeat: no-repeat }

      Ayrıca ikinci değere 0 verseydik yine önceki rengin değeri verilmiş kabul edilirdi. Bu şekilde rengin genişliğini değiştirmek istediğimiz zaman iki değil bir değeri değiştirmemiz yeterli olacak. O yüzden şerit yaparken bu şekilde yapacağız.

.boxx { background-image: linear-gradient( midnightblue 33%, saddlebrown 0, saddlebrown 66%, blueviolet 0, blueviolet 99%); background-size:10px 30px }

      0 değeri verdiğimiz renkler, bir önceki rengin durma noktasıyla aynı durma noktasına sahip olduğu anlamına geliyor. Renkleri birbiri ardınca nasıl uzattığımıza dikket et. Bu şekilde dilediğin kadar şerit oluşturabilirsin. Fakat bunun biraz daha kolay bir yolunu da bulabiliriz.

repeating-linear-gradient()

.boxx { background-image: repeating-linear-gradient(yellow 0, yellow 20px, blue 0, blue 40px) }

      background-size belirtmedik. Ayrıca ilk rengin sıfır değerini belirttik. Eğer renklere hiç bir değer belirtmeseydik önceki fonksiyon gibi işlem yapacaktı.

boxx { background-image: repeating-linear-gradient(yellow, blue) }

      repeating olayında renklere değer verirken, bir önceki rengin değerinden daha büyük değer vermemiz gerekiyor ki işlemi görebilelim.

.boxx { background-image: repeating-linear-gradient(yellow 0px, blue 40px) }

      Renklere verdiğimiz değerler ile belirttiğimiz alan kadar bir gradient oluşturuyor ve elementin geri kalan kısmında da bunu tekrar ediyor. İki renk değerinin farkı gradient'in yüksekliğidir. Yukardaki her bir gradient 40px yüksekliğinde. Eğer gradient soldan sağa olsaydı genişliği 40px olacaktı.

      Şerit olayına dönecek olursak, kullanımı yukarda görmüş olduğun gibi normal fonksiyon ile aynı. Fakat her iki fonksiyonda da renk sayısını arttırınca sıkıntı başlıyor. Mesela 4 renkli bir şerit yapmak istersek şöyle:

.boxx { background-image: repeating-linear-gradient(45deg, goldenrod 0, goldenrod 20px, yellow 0px, yellow 40px,  violet 0, violet 60px, chartreuse 0, chartreuse 80px) }

      Eğer şeritli arkaplanları seviyorsan ve sürekli kullanacaksan, her seferinde bunu böyle sıfırdan başlayıp yazmak bir süre sonra iyice sıkıcı olmaya başlar. Bu yazım tarzını biraz daha makul hale getirmek css önişlemci uygulamalarıyla mümkün. Sass, Less veya (benim favorim) Stylus bu konuda sana çok yardımcı olacaktır. Ben stylus kullanıyorum. Pek fazla stylus bilgim yok ama biraz uğraşarak bg-stripe fonksiyonunu yazdım. Gerçi css profesörleri buna fonksiyon değil mixin diyorlar ama şimdiye kadar programlama dilleriyle uğraştığım için ağız alışkanlığından dolayı fonksiyon diyorum ben. Sen istersen kamyon lastiği diyebilirsin.

See the Pen bg-stripe (with stylus) by hsyn (@hsyn12) on CodePen.

      Bu fonksiyona göre sadece renkleri bildirerek kullanabiliyoruz. Bu şekilde şerit kalınlığı ile yön değeri varsayılan olarak .7em ve 45deg.

.boxx
  bg-stripe(red, green, blue)

      Varsayılan değerleri değiştirmek istersek şöyle yazıyoruz:

.boxx
  bg-stripe(red, green, blue, size:2em)

      Veya:

.boxx
  bg-stripe(red, green, blue, size:1em, direction:135deg)

radial-gradient()

      Bu fonksiyon da yukarıdaki fomksiyonlardan çok farklı değil. En yalın hali şöyle:

.boxx { background-image:radial-gradient(red, yellow) }

      Yazının başında bu fonksiyon için dairesel dedik ama gördüğün gibi tam olarak dairesel değil. Tam olarak dairesel yapmak için:

.boxx { background-image:radial-gradient(circle, red, yellow) }

      Hâlâ net bir şekilde dairesel olup olmadığı tam ortaya çıkmadı ama birazdan çıkacak. Önce w3schools fonksiyonu nasıl tanıtıyor ona bakalım.

radial-gradient(shape size at position, start-color, ..., last-color)

      Bu arada, biz sürekli background-image özelliğini kullanıyoruz ama background özelliğine de yazabiliriz bu gradient'leri. Çünkü gradientler tamamen resim özelliğinde ve resim gibi işlem görüyor. Arkaplana nasıl direk resim atayabiliyorsak gradient de atayabiliriz.

Fonksiyondaki shape parametresi şekil oluyor ve buraya iki şekilden biri girilebiliyor.

  1. ellipse
  2. circle

      Tahmin edebildiğin gibi varsayılan değer ellipse. size parametresinin ise alabileceği dört değer var.

  1. closest-side
  2. farthest-side
  3. closest-corner
  4. farthest-corner

      Tahmin edemeyeceğin üzere varsayılan değer farthest-corner. Tabi bunların ne anlama geldiğini anlamak için bir bakış atmamız gerekiyor. Son olarak at position parametresi olayın nerede patlak vereceğini belirlemek için. Varsayılan değer center. Anlaşıldığı üzere yön belirten anahtar kelimeler kullanılabiliyor, bunun yanında tabiki herhangi bir ölçü birimi de kullanılabilir.

div:nth-child(1) { background: radial-gradient(circle closest-side at 60% 40%, #f00, #ff0)}
div:nth-child(2) { background: radial-gradient(circle farthest-side at 60% 40%, #f00, #ff0)}
div:nth-child(3) { background: radial-gradient(circle closest-corner at 60% 40%, #f00, #ff0)}
div:nth-child(4) { background: radial-gradient(circle farthest-corner at 60% 40%, #f00, #ff0)}
closest-side
farthest-side
closest-corner
farthest-corner

      closest-side değerinde gradient tamamen elementin sınırları içinde kalıyor. Türkçe karşılığı ise en yakın kenar anlamında. Elemente bakınca anlaşılıyor değil mi? Gradient kendine en yakın kenarı baz alıyor ve onun dışına çıkmıyor. Böylece renk geçişi elementin içinde kalıyor. Fakat farthest-side değerinde renk geçişi elementin biraz dışına çıkıyor. Çünkü anlamı en uzak kenar. Bu yüzden renk geçişi biraz daha fazla alana yayılıyor ve elementin dışına çıkabiliyor. Fakat baz aldığı kenarı kesinlikle aşmıyor. Zaten kendini o kenara göre yayıyor. closest-corner ise tabiki en yakın köşe anlamına geliyor ve şekle bakınca da anlaşılıyor derdi. Renk geçişi en yakın köşeyi aşmayacak şekilde yayılıyor. farthest-corner ise en uzak köşe demek ve gördüğün gibi en uzak köşeyi kendine sınır yapıyor ve ona göre yayılıyor renk geçişi.

      Birden fazla gradient de belirtebiliriz haberin olsun. Fakat birden fazla gradient olunca hepsi üstüste bineceği için sadece bir tanesi görünecek. Mantîken son belirtilen gradient'in görünmesi gerekirken ilginç bir şekilde ilk belirtilen gradient görünüyor. Sorun değil. Sorun, renkleri transparan olarak belirtmek zorunda olduğumuz. Renklerin opacity değelerini uygun düzeye indirip kullanmak gerekiyor. Ama hiç bununla uğraşmadan background-blend-mode özelliğine multiply değeri vererek bu işi css'e ihâle edebiliriz.

.boxx {
        background: radial-gradient(at 10% 50%, aqua, burlywood, chartreuse),
                    radial-gradient(at 50% 50%, ghostwhite, palegreen, yellow),
                    radial-gradient(at 90% 50%, lightsteelblue, blueviolet, orangered);
        height: 400px;
        background-blend-mode: multiply;
}

      Ama bunu circle değeri ile yaparsak biraz daha anlamlı olabilir.

.boxx {
    background: radial-gradient(circle at 10% 50%, aqua, burlywood, chartreuse),
                radial-gradient(circle at 50% 50%, ghostwhite, palegreen, yellow),
                radial-gradient(circle at 90% 50%, lightsteelblue, blueviolet, orangered);
    height: 400px;
    background-blend-mode: multiply;
}

Fakat renklere değer verirsek neler olduğunu biraz daha iyi anlayabiliriz.

.boxx {
    background: radial-gradient(circle at 10% 50%, aqua 40px, burlywood 80px, chartreuse 120px),
                radial-gradient(circle at 50% 50%, ghostwhite 40px, palegreen 80px, yellow 120px),
                radial-gradient(circle at 90% 50%, lightsteelblue 40px, blueviolet 80px, orangered 120px);
    height: 400px;
    background-blend-mode: multiply;
}

      Bu şekilde yapacaksak renkleri mümkün olduğunca açık seçmemiz gerek, çünkü renkler birbirine girdikçe koyulaşıyor. Ama bu da ilginç bir durum. Çünkü beyaz diye tâbir ettiğimiz renk tüm renklerin karışımıdır değil mi? Ama burada renkler karıştıkça koyulaşıyor ve arkaplan kararıyor.

.boxx {
    background: radial-gradient(circle at 10% 50%, mediumblue 40px, mediumslateblue 80px, tomato 120px),
                radial-gradient(circle at 50% 50%, maroon 40px, teal 80px, darkgreen 120px),
                radial-gradient(circle at 90% 50%, purple 40px, darkcyan 80px, orangered 120px);
    height: 400px;
    background-blend-mode: multiply;
}

      Olay bu. Tabi böyle tek tek renk belirleyip uğraşacak değiliz. Şuradan kafamıza göre ayrlayıp kullanabiliriz.

      Şimdi gelelim şeritli radial mevzusuna. Yine linear olayında olduğu gibi aynı taktikle dairesel şeritler yapabiliriz. Taktik neydi? Renklere aynı durma noktaları tanımlıyorduk değil mi? Ve bunu yaparken de önceki rengi biraz sonraki renge sündürüyorduk.

.boxx { background: radial-gradient(circle, darkorange 0, darkorange 30px,
                                             red 0, red 60px,
                                             navy 0, navy 90px);
}

      İstediğimiz sonuç tabiki bu değil. Ama neye ihtiyacımız olduğunu tahmin edebildiğini tahmin edebiliyorum.

repeating-radial-gradient()

.boxx { background: repeating-radial-gradient(circle, darkorange 0, darkorange 30px,
                                                      red 0, red 60px,
                                                      navy 0, navy 90px);
}

      Yine tahmin edebileceğin gibi bunu da otomatiğe bağlayabiliriz.

See the Pen radial stripe with stylus by hsyn (@hsyn12) on CodePen.

      Yukarılarda biryerlerdeki linear olayından hiç bir farkı yok. Zaten kodları da ordan kopyalayıp yapıştırdım ve koordinat ile radial kısmını, bir de size değerini değiştirdim hepsi bu. Bu fonksiyona göre, sadece renkleri belirterek elementin ortasından başlayan dairesel şeritler yapabiliyoruz.

.boxx {
      radGrad(red, green, blue);
  }

      Koordinat veya ölçü değiştirmek için ise şöyle:

.boxx {
    radGrad(red, green, blue, coordinate:90% 30%, size:10px);
}

      Ayrıca bu gradient'lerin responsive olduğuna da dikkatini çekmek isterim. Yani ekran küçüldüğünde gradient de küçülüyor. Fakat maalesef gradient'ler animasyonlar ile kullanılamıyor. Yani animatable özelliği taşımıyorlar. Aslında kullanılabiliyor, ama gradient üzerindeki değişimi görebilmek için sayfanın yenilenmesi gerekiyor. Tabi o zaman da animasyonun bir anlamı kalmıyor çünkü olay yine başa dönüyor. Fakat bazı arkadaşlar animasyon etkisi verebilmek için akıllıca bir iş yapmışlar. background-position değerini animasyon ile değiştirerek sanki gradient değişiyormuş gibi bir hava yaratmışlar. Bu aleti de buradan kurcalayabilirsin.

border gradient

      Sıra geldi gradient'leri border olarak kullanmaya. Aslında bunun için border-image özelliği yapmışlar ama biz şu kitabın 71. sayfasındaki yöntemi kullanacağız. Bunun için bazı ön bilgilere ihtiyacımız var. Mesela background-clip ve background-origin özelliği. Bu özellik(background-clip), elementin arkaplan renginin nereden başlayacağını belirtiyor. Varsayılan değeri border-box. Yani elementin arkaplan rengi border çizgileri de dahil olamak üzere boyanıyor. background-origin ise arkaplan resminin nereden başlayacağını belirtiyor. Varsayılan değeri padding-box. Yani arkaplan resmi elementin border çizgileri hariç, padding boşluğu dahil olmak üzere elemente yerleşiyor. Bu iki özelliğin alabileceği değerler aynı.

  • border-box (background-clip varsayılan)
  • padding-box (background-origin varsayılan)
  • content-box

      Aradaki fark, background-clip arkaplan boyama alanını, background-origin arkaplan resim alanını kastediyor. Bu bilgiler ışığında olayımız, arkaplana bir gradient atayıp, background-origin özelliğini padding-box'tan border-box'a geçirerek bir alicengiz oyunu yapmak. Tabi ambiyansa uygun border ve padding değerleri de vereceğiz. İki tane gradient'imiz olacak, biri arkaplan renginde ve origin değeri padding-box olacak. Diğeri ise şeritli olacak ve border-box değerinde. En iyisi görelim şunu.

.boxx {
        background: #333;
        height: 15em;
        padding: 1em;
        border: 1.5em solid transparent;
        box-sizing: border-box;
        background: linear-gradient(#333, #333) padding-box,
                    repeating-linear-gradient(45deg,
                                                red 0, red .5em,
                                                #333 0, #333 1em,
                                                green 0, green 1.5em,
                                                #333 0, #333 2em) border-box;
}
Nibh maecenas! Culpa! Natoque. Lorem, amet, blandit eaque minima! Rerum do, integer praesent, dis non? Dis integer aliquip, perferendis facilis, illo, egestas, cupiditate atque voluptas sodales pellentesque, hic turpis tristique pharetra ipsa nullam porttitor neque, hac gravida modi! Beatae harum diam aliquip? Voluptate cursus, mattis! Vel, magnam dignissimos consectetuer nemo? Facilisi, iaculis montes nobis, magnam laboris quia amet reprehenderit dignissimos voluptates orci nihil provident, tempor suspendisse! Asperiores sequi? Corporis incididunt pariatur molestias iusto pariatur nec. Molestias maxime tempora nullam repellat! Maiores incididunt eleifend augue dis dolore ad dolor, curae doloribus! Tempora ab! Repellendus voluptas natus at pariatur sagittis ad nonummy.

      Kitaptakinden biraz farklı yaptık, çünkü kitabı yazan ablamız arkaplan rengini tam hesap etmemiş. Border olarak yaptığımız ikinci gradient'te renkler arasında boşluk oluşturmak için arkaplan rengini kullanmak gerekiyor. Yazar ise transparent değeri kullanmış. transparent değeri yanlızca beyaz arkaplanda çalışır, değişik renkteki arkaplanlarda cırt olur. Aslında bu noktada background özelliğinin de tam olarak nasıl tanımlandığına bir bakmak gerek.

background: color image position/size repeat origin clip attachment

      Biz buradaki image ve origin değerlerini kullandık. Bu durumda anlaşılması pek de zor olmayan bir işlem gerçekleştirmiş olduğumuz da ortaya çıkıyor. Resmin birini border'a, diğerini padding'e dayıyoruz, dolayısıyla birinin kenarı border'da gözüküyor, diğeri ise daha içerde kalıyor. Çünkü 1.5em border, ve 1em de padding verdik. Ve border transparent olduğu için görünmez durumda. Haliyle altındaki gradient origin olarak border-box kullanınca o gradient görünüyor. Normalde bu origin'in padding-box olduğunu yukarda belirtmiştik değil mi?

      İstersen son olarak bunu da otomatikleştirelim.

See the Pen bbox (border gradient with stylus) by hsyn (@hsyn12) on CodePen.

      bbox fonksiyonu yukarıda yazdığımız fonksiyonlardan biraz daha gevşek bir fonksiyon. Çünkü bu olayda değiştirilebilecek çok fazla özellik var. O yüzden fonksiyonu daha fazla çorba etmemek için bazı özellikleri direk elemente vermemiz gerekiyor. Mesela border ve padding değerini. Fonksiyon daha çok gradient üzerine yoğunlaşmış durumda. İstediğin kadar renk belirtebilirsin, fonksiyon hepsinin arasına bir boşluk bırakarak sıraya diziyor. Daha doğrusu aralara arkaplan rengini yerleştiriyor. Böylece sanki boşluk varmış gibi gözüküyor.

Sanırım bir birlikteliğin sdaha sonuna gelmiş bulunmaktayız. Yayında ve yapımda emeği geçen tüm arkadaşlarım adına esenlikler dilerim. Hoşçakalın, esenkalın, kaybolun..