איך לכתוב jQuery Plugin

מנגנון הפלאגינים היה אחד הרעיונות הראשונים ב jQuery ואולי הסוד שהביא לתפוצה האדירה של ספריה זו. פלאגינים הם יחידת שיתוף קוד ב JavaScript שקל לבנות אותם, קל לחלק אותם לחברים וקל להשתמש בפלאגינים של אחרים ביישום שלכם.

אפשר לחשוב על פלאגין בתור פונקציה שמקבלת כפרמטר איזשהו DOM Element. וזה כנראה יהיה די קרוב למציאות. אנחנו מצרפים פלאגין ליישום באמצעות הדבקת פונקציה כזו על שדה מסוים ב jQuery Object ומשם אפשר להפעיל את הפלאגין על כל אלמנט בעמוד.

1. הפלאגין הראשון שלי

דוגמא ראשונה ופשוטה לפלאגין יכולה להיות קוד שבוחר צבע אקראי לאלמנט. ב jQuery אנחנו יכולים לשנות צבע טקסט עם הפקודה css ולכן אם יש לכם כבר פיסקת טקסט בעמוד תוכלו להפעיל את הקוד הבא כדי לבחור לה צבע באופן אקראי:

const colors = ['red', 'blue', 'green', 'cyan', 'orange', 'magenta', 'grey', 'brown'];

$('.text').css('color', _.sample(colors));

(בהנחה כמובן ששמתם בדף גם את lodash המופלאה).

בשביל להפוך את זה ל jQuery Plugin צריך להוסיף מעט מאוד:

  1. יש לרשום את הקוד בתוך פונקציה

  2. יש להדביק את הפונקציה לאוביקט jQuery בשדה fn שלו.

כך נראה הקוד ליצירת הפלאגין:

const colors = ['red', 'blue', 'green', 'cyan', 'orange', 'magenta', 'grey', 'brown'];

$.fn.randomColor = function() {
  this.css('color', _.sample(colors));
};

בשביל להפעיל את הפלאגין קוראים לו כמו לכל פונקציית jQuery רגילה:

$('.text').randomColor();

ועכשיו כל אחד שתתנו לו את קובץ הפלאגין יוכל גם להפעיל את הפלאגין ולבחור צבע אקראי לכל אלמנט בעמוד.

ככה זה נראה לייב בקודפן:

2. פרמטרים לפלאגין

לא קשה לדמיין את היום שאנשים ירצו להעביר את רשימת הצבעים לפלאגין מבחוץ. לאורך השנים והפלאגינים פותחה דרך סטנדרטית בשביל זה המבוססת על אוביקט ברירות מחדל. הרעיון שבתוך הפלאגין אנחנו מגדירים אוביקט עם כל הגדרות ברירת המחדל. בנוסף פונקציית הפלאגין מקבלת פרמטר של אפשרויות שגם הוא אוביקט שיכול להכיל חלק או יותר מאפשרויות ברירת המחדל. הפלאגין מחבר בין שני האוביקטים ומשתמש בתוצאה כדי לקבוע את התנהגותו.

בדוגמא שלנו נוכל להגדיר אוביקט ברירת מחדל שכולל את רשימת הצבעים והאם להחליף צבע באופן אוטומטי כל מספר שניות. מתוך קובץ הפלאגין נכתוב:

$.fn.randomColor.defaults = {
    colors: ['red', 'blue', 'green', 'cyan', 'orange', 'magenta', 'grey', 'brown'],
    auto: false,
};

בנוסף נרצה לאפשר להעביר לפונקציית הפלאגין את שני הפרמטרים ולכן נעדכן אותה שתיקח פרמטרים מבחוץ ותשלב אותם עם ערכי ברירת המחדל. לאחר העידכון קובץ הפלאגין שלנו כולל את התוכן הבא:

$.fn.randomColor = function(options) {
  const opts = Object.assign({}, $.fn.randomColor.defaults, options);
  this.css('color', _.sample(opts.colors));
};

$.fn.randomColor.defaults = {
  colors: ['red', 'blue', 'green', 'cyan', 'orange', 'magenta', 'grey', 'brown'],
  auto: false,
};

ועכשיו בהפעלת הפלאגין יש לנו שתי יכולות חדשות. אנחנו יכולים לעדכן מבחוץ את אוביקט ערכי ברירת המחדל כדי לקבל סט צבעים שונה לכל היישום שלנו:

$.fn.randomColor.defaults.colors = ['red', 'blue'];
$('.text').randomColor();

או שאנחנו יכולים להעביר סט צבעים רק בהפעלת הפלאגין על אוביקט מסוים ואז לאוביקטים מסוימים יהיו סטים שונים של צבעים לבחור מהם:

$('.text').randomColor({ colors: ['red', 'blue' ]});

קוד הפלאגין והשימוש בו נמצאים בקודפן הבא. נסו לבד להוסיף את התמיכה ב auto כך שיחליף צבעים באופן אוטומטי:

<iframe height='300' scrolling='no' title='rqRVLE' src='//codepen.io/ynonp/embed/rqRVLE/?height=300&theme-id=20374&default-tab=js,result' frameborder='no' allowtransparency='true' allowfullscreen='true' style='width: 100%;'>See the Pen <a href='https://codepen.io/ynonp/pen/rqRVLE/'>rqRVLE</a> by Ynon Perek (<a href='https://codepen.io/ynonp'>@ynonp</a>) on <a href='https://codepen.io'>CodePen</a>.
</iframe>

3. יצירת DOM וטיפול באירועים

פלאגינים יותר מורכבים עושים עוד שני דברים בנוסף למה שראינו:

  1. הם יוצרים מבנה DOM מורכב בעצמם על האוביקט שהעברנו.

  2. הם מוסיפים Event Handlers כך שהמבנה החדש גם יתפקד בצורה יצירתית.

נרשום דוגמא נוספת לפלאגין הפעם שיוצר תיבת טקסט שמוגבלת לכתיבת 140 תווים, אבל גם שמציגה את מספר התווים שנשארו להקליד מעל התיבה. בשביל ליצור את ה DOM Object בצורה נוחה ומאובטחת אפשר להשתמש ב safeHtml מתוך common-tags:

$.fn.limitedTextbox = function(options) {
  const opts = Object.assign({}, $.fn.limitedTextbox.defaults, options);
  this.html(safeHtml`
  <div class='limited-textbox'>
    <div>You have <span class="remaining">${opts.maxlen}</span> characters left</span></div>
    <div><textarea maxlength=${opts.maxlen}></textarea></div>
  </div>
  `);
  $('.limited-textbox').on('input', update);
};

$.fn.limitedTextbox.defaults = {
  maxlen: 140,
};

ובשביל עדכון התיבה אחרי כל אירוע input נוכל להשתמש בפונקציה הבאה:

function update() {
  const root = this;
  const $remaining = $('.remaining', root);
  const textarea = $('textarea', root);
  const maxlen = Number(textarea.attr('maxlength'));
  const textlen = Number(textarea.val().length);
  $remaining.text(maxlen - textlen);
}

4. עדכון הפלאגין מבחוץ

אתגר נוסף של כותבי פלאגינים הוא לאפשר לקוד חיצוני לעדכן את הפלאגין מבחוץ. בדוגמא שלנו יכול להיות שקוד חיצוני רוצה לכתוב (מתוך קוד JavaScript) טקסט בתוך התיבה. אי אפשר סתם כך לכתוב את הטקסט כי אז מונה התווים לא יתעדכן.

הפיתרון שנמצא לזה הוא להעביר את הפקודה בתור פרמטר אופציונאלי לפונקציית הפלאגין. בדוגמא שלנו נוכל לכתוב את השורות הבאות בתחילת פונקציית הפלאגין כך שתאפשר העברת פרמטרים או פקודה מבחוץ, ואם התקבלה הפקודה update תרשום את הטקסט בתיבה:

$.fn.limitedTextbox = function(...options) {
  if (options.length === 2) {
    if (options[0] === 'update') {
        return updateText(this, options[1]);
    }
  } else if (options.length === 1) {
    return init(this, options);
  } else {
    return init(this);
  }
};

קוד הפלאגין במלואו זמין למשחקים בקודפן הבא:

5. עכשיו אתם

חושבים שהבנתם את הרעיון? הנה כמה פלאגינים שאתם יכולים לנסות לכתוב. תרגישו חופשי לשים קישורים לקודפנים עם הפלאגינים כאן בתגובות:

  1. פלאגין עבור Spoiler Alert: הפלאגין מוחק את הטקסט מ DOM Element שהועבר לו ובמקום הטקסט מציג כפתור "אזהרת ספוילר - לחצו כדי לחשוף את הטקסט". אחרי לחיצה הטקסט המקורי מתגלה.

  2. פלאגין להצגת דיאלוג מודאלי: הפלאגין מעלים את האלמנט שנתתם לו ומאפשר מתוך JavaScript להעביר לו שתי פקודות. הפקודה show גורמת להצגת האלמנט בצורת דיאלוג מודאלי והפקודה hide גורמת להסתרת האלמנט שוב.

  3. פלאגין להצגת גודל המסך הנוכחי: בכל פעם שמישהו משנה את גודל החלון יש להציג בתוך האלמנט את מימדי החלון הנוכחיים.