ארכיון פוסטים מהקטגוריה "CSS"
OhBehave – החלת התנהגות על אלמנט סטטי או דינמי בצורה מיידית
יום שישי, 18 בדצמבר, 2009באפליקציות ווב ואתרים עשירים, לעיתים קרובות יש צורך בהחלת התנהגות כלשהי על אלמנט.
דוגמאות:
- הפיכת תיבת select לפקד autocomplete
- השמת אלמנט בתוך מסגרת מורכבת (פינות מעוגלות, שקיפות)
- החלת אירועי לחיצה על אלמנטים מסוג מסויים
- השמת תווית צפה מעל פקד טקסט עד ל-focus/שינוי ערכו
פיתרון סטנדרטי יהיה להמתין לעליית ה-dom (פונקציה אשר מצוייה בכל ספריית js אשר מכבדת את עצמה), לרוץ על אלמנטים עם class מסויים, ולהפעיל פונקציית js שתחיל את ההתנהגות.
שימו לב! במידה ומדובר בהצמדת אירועים בלבד כדאי מאוד להשתמש בשיטת ה-Event Delegation
חסרונות השיטה:
- לפעמים, לוקח מעט זמן עד שה-dom עולה והאלמנט מופיע בגרסתו נטולת ההתנהגות עד לסיום הטעינה
- כאשר מוסיפים דינמית אלמנטים לעמוד (innerHTML או createElement & appendChild) צריך לדאוג להחיל את ההתנהגות שוב, על כל האלמנטים שדורשים זאת
- כפיתרון גנרי לאלמנטים דינמיים אפשר להפעיל טיימר על העמוד שיחקור את ה-dom בכל פעם מחדש. זהו תהליך איטי, וב-delay בקריאות ל-interval עלולות להיות קפיצות
דוגמה לעבודה בשיטה זו:
<script type=”text/javascript”>
function applyBehaviors() {
var elements=$.select(“.behavior”);
for (var i=0;i<elements.length;i++) applySpecificBehavior(elements[i]);
}
$.domready(function () {
applyBehaviors();
});
$.ajax("url.html",
onSuccess:function () {
applyBehaviors();
}
);
</script>
(הפונקציות $.select, $.domready, $.ajax הינן פונקציות פיקטיביות המקנות אפשרות לבחור אלמנטים לפי css selector, לבצע פעולה מייד בעליית ה-dom וביצוע בקשת ajax, בהתאמה. ניתן למצוא אותן בכל ספריית javascript המכבדת את עצמה).
הפיתרון הנכסף – OhBehave
OhBehave הינו סקריפט חביב, שאחראי על החלת התנהגות על אלמנטים באופן מיידי, ברגע הראשון בו הם זמינים. מבלי להמתין לטעינת שאר העמוד. בנוסף, מחיל את ההתנהגות גם על אלמנטים חדשים הנוצרים דינמית.
איך זה עובד?
לכל דפדפן נאלצתי למצוא פיתרון שונה, מאחר וכל אחד מיישם את הפונקציונליות בה רציתי באופן אחר. מתוך כל מנגנון בחרתי להפעיל פונקציה בשם OhBehave.initialize שמקבלת אלמנט ותניע את החלת ההתנהגויות.
התנהגויות האלמנט מוגדרות אף הן ב-class שלו. לדוגמה – oh-behave-WrapWithFrame, oh-behave-Autocomplete (שימו לב לקידומת oh-behave-).
כל התנהגות היא פונקציה המאוחסנת באובייקט OhBehave.behaviors שמקבלת אלמנט עליו היא מחילה התנהגות בודדת.
לא רציתי לכבול לשימוש ב-OhBehave, כך שניתן להגדיר את הפונקציה גם כגלובאלית, ואף ניתן לשנות את קידומת ההתנהגות (ראו בקובץ המצורף).
דוגמה:
OhBehave.behaviors["AlertTheTime"]=function (element) {
element.onclick=function () {
alert(new Date());
};
};
// or -- function AlertTheTime(element) { … }
Firefox (Gecko Engine) – שימוש ב-XBL
XBL או XML Binding Language היא שפת תגיות מבוססת XML המוגדרת בתקן W3C להחלת התנהגות עשירה לאלמנט. ה-XBL מאפשר להגדיר מאפיינים (כולל getters/setters), אירועים וירייתם, מתודות וקוד CSS/JS שיחול על האלמנט. ה-XBL מוצמד בעזרת css בתצורה:
.oh-behave { -moz-binding:url("oh-behave.xml#oh-behave"); }
ומבנה ה-xml:
<bindings xmlns="http://www.mozilla.org/xbl" xmlns:html="http://www.w3.org/1999/xhtml">
<binding id="oh-behave">
<implementation>
<constructor>OhBehave.initialize(this);</constructor>
</implementation>
</binding>
</bindings>
כרגע רק מנוע Gecko תומך ב-XBL, על אף שהוא בתקן.
הצמדתי XBL ל-css class מסויים ומתוכו הפעלתי קוד גנרי המחיל התנהגות.
IE (Trident Engine) – שימוש ב-HTC
HTC, או HTML Component הינו יישום של Microsoft להחלת התנהגות עשירה לאלמנט. הוא מאפשר פחות או יותר את מה שה-XBL מאפשר.
Safari/Chrome (WebKit Engine) – האירוע DOMNodeInserted
מאחר וב-WebKit עוד לא הכניסו את XBL לשימוש (בקוד המקור יש אזכורים אך כנראה האופציה עדיין כבוייה), נאלצתי למצוא פיתרון אחר. האירוע DOMNodeInserted (אירוע המוגדר בתקן) נורה כאשר אלמנט מוכנס לעמוד דינמית (innerHTML / createElement & appendChild). דרך אירוע זה אני מחפש את האלמנטים עליהם יש להחיל את ההתנהגות. חיפוש האלמנטים מתבצע בעזרת querySelectorAll – מתודה של אלמנט או מסמך, המקבלת css selector ומגישה את כל האלמנטים העונים על סלקטור זה, תחת אותו אלמנט.
בעליית העמוד השתמשתי באירוע DOMContentLoaded שרץ מייד בעליית ה-dom. כאן עלולה להיות קפיצה קטנה, אבל המנוע כל-כך מהיר שאני לא חושב שיהיה אפשר להבחין בה.
דוגמה חיה
בעמוד הדוגמה ניתן לראות החלת שתי התנהגויות על אלמנט: הראשונה מקיפה אותו במסגרת הכוללת אלמנטים נוספים המרכיבים אותה, השניה מצמידה אירוע לחיצה המציג את הזמן הנוכחי.
בנוסף, כפתור אשר יוצר מוסיף אלמנטים חדשים למסמך בצורה דינמית, וגם עליהם חלות ההתנהגויות, Automagically.
תיקון PNG ל-IE6
יום רביעי, 5 בנובמבר, 2008כתבתי קוד לתיקון png על IE, שעובד עם css expression. לא – אין מה לדאוג, לא מדובר ב-css expression שירוץ כל אירוע שקורה בעמוד, אלא רק פעם אחת עבור כל תמונה או אלמנט עם class=png. בנוסף, ישנה תמיכה בשינוי תמונה בצורה דינמית.
img,.png {
behavior:expression((function () {
window.__isPng=window.__isPng || function (src) { return src.toLowerCase().indexOf('.png')>-1; };
window.__fixPng=window.__fixPng || function (el) {
var pngSource,sizingMethod;
if (el.nodeName.toLowerCase()=="img" && window.__isPng(el.src)) {
pngSource=el.src;
sizingMethod="image";
el.src="transparent.gif";
} else if (window.__isPng(el.currentStyle.backgroundImage)) {
pngSource=el.currentStyle.backgroundImage.replace('url("','').replace('")','');
sizingMethod=el.currentStyle.backgroundRepeat=="no-repeat" ? "crop" : "scale";
el.runtimeStyle.backgroundImage="none";
}
if (pngSource) el.runtimeStyle.filter="progid:DXImageTransform.Microsoft.AlphaImage"+"Loader(src='"+pngSource+"', sizingMethod='"+sizingMethod+"')";
};
this.runtimeStyle.behavior="none";
var that=this;
this.attachEvent("onpropertychange",function (e) {
if (e.propertyName=="src") window.__fixPng(that);
});
window.__fixPng(this);
}).call(this));
}
קצת הסברים:
css expression, יכול להכיל קוד javascript לכל דבר. ההתייחסות ל-this בתוכו, היא התייחסות לאלמנט עצמו. לא מומלץ להשתמש בו מאחר והוא יפעל בכל אירוע בדפדפן. ראו כאן. בהמשך אסביר איך השימוש שלי לגיטימי.
בפונקציה window.__isPng אנחנו נגלה האם path של תמונה מכיל png.
הפונקציה window.__fixPng מקבלת אלמנט, בודקת אם הוא מסוג תמונה או כל אלמנט אחר, בהתאמה בודקת את ה-src או ה-background-image שלו ומחילה עליו filter של AlphaImageLoader. כאשר מדובר בתמונה, נאלץ להשאיר לה src כדי לא להחליף אותה באלמנט אחר. לשם כך נשתמש בתמונה המכילה gif שקוף של פיקסל.
היות הביטוי x=x || function אומר – x שווה ל-x או לפונקציה. ה-או אומר, אם האיבר הראשון הינו שווה ערך ל-false (שזה אומר false, 0, null, undefined או מחרוזת ריקה), תן את האיבר השני, כך הגדרת הפונקציה קורית רק פעם אחת למרות שבפועל כל הקוד שב-expression רץ. אחרי הפעם הראשונה מה שקורה אחרי ה-|| לא מתבצע כי x כבר לא שווה ערך ל-false.
this.runtimeStyle.behavior="none";
קוד זה הוא בעצם ה"תרופה" ל-css expression. הוא יקרה רק פעם אחת לאלמנט ומייד ייעלם.
sizingMethod=el.currentStyle.backgroundRepeat=="no-repeat" ? "crop" : "scale";
טוב, אז אין באמת תמיכה ב-repeat של background, לשם כך היינו צריכים ליצור מספר אלמנטים ולמקם אבסולוטית תוך כדי חישוב המיקומים ליצירת גריד של תמונה אחת משוכפלת. ההיגיון מאחורי השורה – תמונות עם repeat תימתחנה. זה נועד ספציפית לתמונות רקע שאמורות לחזור על עצמן כמו בורדרים של אלמנט שצריכים להתרחב בהתאם לתוכן שלו.
var that=this;
this.attachEvent("onpropertychange",function (e) {
if (e.propertyName=="src") window.__fixPng(that);
});
קוד זה בא לענות על צורך של שינוי דינמי של src של תמונה. השימוש ב-onpropertychange נורה כאשר כל מאפיין שונה. בעזרת propertyName נבדוק האם שונה המאפיין src. אירוע זה עובד רק על IE.
מאחר וכל פונקציה מקבלת this משלה, ו-this שבתוך attachEvent הוא אינו האלמנט אלא החלון (למה?!), נאלץ לשמור אותו ברפרנס בשם that.
לבסוף, את הקוד נכליל בעזרת conditional comment ל-IE6 ומטה:
<!--[if lte IE 6]> <link rel="stylesheet" href="fixpng.css" type="text/css"/> <![endif]-->
מקווה שתמצאו קוד זה שימושי, אם כי לא לזמן רב מדי. אני מייחל מוות מהיר לשימוש ב-IE6
איפוס CSS
יום שישי, 13 באפריל, 2007באופן טבעי, לכל דפדפן הגדרות ברירת מחדל שונות ל-CSS. הריווחים בין תגיות p, ריווחים מקצוות המסך, גדלי פונטים לתגיות ה-header.
כרגיל, אין תקן, ובחלק מהמקרים, אנחנו המפתחים נתקלים בקשיים ובסוף מגלים שהתמונה למטה נדחפת כי בין כל אחת מ-6 הפסקאות שמעליה ב-IE יש רווח גדול יותר מאשר ב-FF.
YUI, ספריית הקליינט סיד של yahoo, מכילה קובץ שמבצע Reset לכל התכונות האלו. להלן הקוד עם תוספות מיניאטוריות:
html,body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,form,fieldset,input,textarea,p,blockquote,th,td {
margin:0;
padding:0;
}
table {
border-collapse:collapse;
border-spacing:0;
}
fieldset,img {
border:0;
}
address,caption,cite,code,dfn,em,strong,th,var {
font-style:normal;
font-weight:normal;
}
ol,ul,li {
list-style:none;
list-style-type:none;
}
caption,th {
text-align:left;
}
h1,h2,h3,h4,h5,h6,th,td {
font-size:100%;
font-weight:normal;
}
q:before,q:after {
content:'';
}
abbr,acronym {
border:0;
}
לאחר הכללת הקובץ, כל העמוד שלכם יצטמצם, אבל כעת תוכלו לכתוב ידנית את הריווחים ולדעת אותם במדויק, עבור כל הדפדפנים.
יאהו נותנים לבצע link לקובץ מתוך השרתים שלהם:
<link rel="stylesheet" type="text/css" href="http://yui.yahooapis.com/2.2.2/build/reset-fonts-grids/reset-fonts-grids.css" />
באגים נפוצים ב-IE6
יום רביעי, 10 בינואר, 2007דפדפן Interner Explorer בגרסה 6 היה שיפור עצום מבחינת תמיכה בתקנים מאז ימי 5 הזוהרים (ואף מילה על 5.5). ה-CSS התחיל להתנהג בצורה תקנית בעזרת DOCTYPE מתאים וגם ה-dom התחיל להיות תואם להגדרות W3C.
עם זאת, הרבה פעמים אנו נתקלים בבאגים מרגיזים שיכולים לעכב את העבודה שלנו כדי למצוא workarounds. מה לעשות ש-IE7 ו-Firefox עדיין לא השתלטו על השוק, ו-IE6 תופס נתח לא קטן, ואנחנו כמתכנתי web צריכים להתאים את האתרים שלנו לכל הדפדפנים הנפוצים.
אספתי כמה באגים כאלה, שבכולם נתקלתי במהלך העבודה בשנים האחרונות, ודרכים לפתרון.
#1 : ערכי margin כפולים ב-float
לכל אלמנט שחל עליו float כלשהו, ערכי ה-margin שיחולו עליו יוכפלו. אין הסבר הגיוני לזה.
דוגמה:
<div style="float:left;margin-left:100px;">IE6 doubles the margin and renders margin-left with 200px</div>

פתרון:
הצורה הכי אלגנטית לפתור את הבאג הזה היא לתת מאפיין display:inline לאלמנט בעל ה-float. הגדרת ה-float בכל מקרה משכתבת את ה-display ל-block. זה פותר את הבעיה, ולא מונע מהאלמנט להמשיך לתפקד כמו block.
#2 : תזוזה של פיקסל כאשר רוחב/גובה אי זוגיים
כאשר ממקמים אבסולטית אלמנט בתוך אלמנט אחר (בעל position:relative) ורוחבו או גובהו של אלמנט האב אי זוגיים, האלמנט הפנימי יזוז פיקסל אחד אחורה
<div style="width:199px;height:99px;position:relative;border:1px solid #000;"> <div style="width:100px;position:absolute;background-color:red;right:0;bottom:0;"></div> </div>
ניתן לראות כי יש רווח של פיקסל מימיןומלמטה
פתרון:
לבאג זה לא מצאתי ממש פתרון קסם, אך בעזרת ה-underscore hack ניתן למשוך את האלמנט למיקום הנכון שלו:
<div style="width:199px;height:99px;position:relative;border:1px solid #000;"> <div style="width:100px;position:absolute;background-color:red;right:0;bottom:0;_margin-bottom:-1px;_margin-right:-1px;"></div> </div>
#3 : תמונות וצבעי רקע נחתכים
את מי שלא מכיר דרכי פתרון, הבאג הזה יכול להוציא מהכלים. קצת הסבר כללי – ב-css של IE יש מאפיין readonly שנקרא hasLayout. לאלמנט "יש" layout אם באחד המאפיינים הבאים יש ערך שונה מברירת המחדל: width, height, float, zoom, או אחד מהמאפיינים הבאים: display:inline-block, position:absolute, writing-mode:tb-rl.
הבאג עצמו חל על אלמנטים ללא layout וגורם להתנהגויות מוזרות: צבעי ותמונות רקע חתוכים או לא מופיעים, טקסט שנעלם ומופיע בסימון, טקסט שמיקומו נדחף.
הפתרון, כאמור, הוא לתת אחד מהמאפיינים שלמעלה. מכיוון שכל המאפיינים משפיעים על צורתו ומיקומו של האלמנט ולא תמיד זה מה שנרצה, נוכל להשתמש במאפיין zoom עם ערך של 1. ה-zoom לא באמת ישתנה, אך האלמנט יקבל layout והבאג ייפתר.
ul li {
background-image:url('image.gif');
zoom:1;
}
#4 : opacity, טקסט ו-ClearType (גם ב-IE7)
windows xp מגיעה עם אופצית ריכוך גופנים, כאשר הריכוך הקל יותר מופעל כברירת מחדל. כשמחילים opacity על אלמנט כשבתוכו טקסט מודגש, הטקסט הופך מגורען.
בדוגמה אפשר לראות טקסט בתוך div עם שקיפות של 40%. הטקסט המודגש נראה מגורען ולא חלק.
פתרון:
צבע רקע כלשהו לאלמנט המכיל פותר את הבאג.
#5 : תיבת select עולה על אלמנטים
ללא התחשבות ב-z-index, תיבות בחירה ב-IE6 עולות על כל אלמנט (כמעט).
אם יש לי תפריט שנפתח במעבר עכבר, ומתחת לאיזור בו אמור להפתח התפריט יש select, התיבה תופיע מעל אותו אלמנט.
פתרון:
אלמנט אחד ש-select לא עולה עליו הוא iframe. אבל אלמנטים אחרים כן יכולים לעלות על iframe. המסקנה המתבקשת היא – הצגת iframe מעל ה-select והצגת האלמנט מעל אותו iframe. את ה-iframe אפשר לעשות שקוף בעזרת opacity של 0. את המיקום והגודל של ה-iframe יש להעתיק מהאלמנט אותו אנו חפצים לשים מעל ה-select.
> html
<iframe id="hider-iframe" style="display:none" frameborder="0" scrolling="no"></iframe>
> css
#hider-iframe {
position:absolute;
filter:alpha(opacity=0);
}
> js
function ShowMenu() {
HiderIframeShow(menuElement);
menuElement.style.display="";
}
function HiderIframeShow(clone) {
var hiderIframe=document.getElementById("hider-iframe");
hiderIframe.style.top=clone.style.top;
hiderIframe.style.left=clone.style.left;
hiderIframe.style.width=clone.offsetWidth+"px";
hiderIframe.style.height=clone.offsetHeight+"px";
hiderIframe.style.display="";
}
מאפייני ה-top/left מועתקים מערכי האלמנט, בהנחה שגם ה-iframe וגם האלמנט בעלי position:absolute ויושבים תחת אותו offsetParent (הא הראשון בעל position:absolute/relative או ה-body). אם לא כך הדבר – יש להשתמש במנגנון המחשב מיקום מדוייק של האלמנט.
#6 : צבעים של PNG שקוף משתנים
נתקלתי לאחרונה בתופעה מוזרה: כאשר משתמשים ב-AlphaImageLoader filter של IE6 להצגת תמונות PNG עם שקיפות, התמונות התמונות מוצגות כהות יותר ממה שהן במקור. כן, IE6 ממש משנה ערכי RGB על התמונה.
זה רע כשהתמונה צריכה לשבת/להתחבר עם סטריפ בעל צבע רקע שאמור להיות כביכול המשכי לתמונה.
לאחר מחקר קטן גיליתי שאכן יש באג במידה וה-PNG יוצאה מפוטושופ (כנראה גם בעורכי תמונות אחרים). מצאתי כלי חינמי המתקן את העוולה -
TweakPNG. לאחר ההפעלה של התוכנה פותחים את קובץ ה-PNG, מוחקים את ה-Chunk בשם gAMA, ושומרים.
(מיותר לציין, Firefox ו-IE7, בעזרת תגית img או background-image, עומדים בגבורה במשימה ומציגים צבעים נכונים מראש).
underscore hack
בהתקלות בכל באג נוסף שמצריך טיפול מיוחד רק ב-IE6 אפשר להשתמש ב-underscore hack: כל מאפיין css שיוצמד לו מקף תחתון לפני שמו – יוחל רק ב-IE6.
.className {
background-color:red;
_background-color:blue;
}
בכל דפדפן מלבד IE6 נראה צבע אדום, אך IE6 כן יתייחס ל-_background-color כמאפיין חוקי, ולכן צבע הרקע יהיה כחול.
כדאי לצמצם שימוש בהאקים, ולמצוא פתרון אחיד לכל הדפדפנים.