» Publishers, Monetize your RSS feeds with FeedShow: More infos (Show/Hide Ads)
סוגי רכיבים נפוצים הם תמונה, קובץ css, קובץ javascript, פניית xmlhttp. לכל אחד מאלה יש אירוע בו אנו יודעים כי הוא נטען, אך עצם העובדה שהוא נטען לא אומרת שהוא התחיל להשפיע על העמוד שלנו.
לדוגמה:
- טעינה בזמן ריצה של קובץ css והשמתו על העמוד לא אומרת שמיידית הוא ישפיע על האלמנטים.
- קבלת html מתוך xmlhttp והשמתו על העמוד לא אומרת שמיידית כולו רונדר וניתן לגשת לרכיבים בו ללא בעיה
- טעינת קובץ javascript לעמוד לא אומרת שכל האובייקטים בו מוכנים לשימוש
לשם מצבי קצה אלו כתבתי פונקציה פשוטה בבסיסה, שממתינה עד אשר פעולה כלשהי בוצעה ורק אז מריצה קוד נוסף.
/// $waitUntil
/// waits until a certain function returns true and then executes a code. checks the function periodically
/// parameters
/// check - a function that should return false or true
/// onComplete - a function to execute when the check function returns true
/// delay - time in milliseconds, specifies the time period between each check. default value is 100
/// timeout - time in milliseconds, specifies how long to wait and check the check function before giving up
function $waitUntil(check,onComplete,delay,timeout) {
// if the check returns true, execute onComplete immediately
if (check()) {
onComplete();
return;
}
if (!delay) delay=100;
var timeoutPointer;
var intervalPointer=setInterval(function () {
if (!check()) return; // if check didn't return true, means we need another check in the next interval
// if the check returned true, means we're done here. clear the interval and the timeout and execute onComplete
clearInterval(intervalPointer);
if (timeoutPointer) clearTimeout(timeoutPointer);
onComplete();
},delay);
// if after timeout milliseconds function doesn't return true, abort
if (timeout) timeoutPointer=setTimeout(function () {
clearInterval(intervalPointer);
},timeout);
}
POC:
var globalVariable=0;
setTimeout(function () { globalVariable=1; },2000);
$waitUntil(
function () {
console.log("checking globalVariable="+globalVariable);
return globalVariable==1;
},
function () {
alert("done!");
}
);
בדוגמה אנו יכולים לראות שיש משתנה בשם globalVariableשמאותחל ל-0, ולאחר 2 שניות ערכו הופך ל-1 ידנית. הפונקציה $waitUntil מקבלת שתי פונקציות. האחת תחזיר true/false לפי בדיקה שתבצע, והשניה תבצע קוד שירוץ רק אחרי שהראשונה החזירה true.
* הפונקציה console.log שייכת ל-firebug/webkit/ie8 ולא נתמכת ב-ie6/7.
דוגמאות לשימושים יותר נפוצים:
ajax("url.htm",function (source) {
element.innerHTML=source;
$waitUntil(
function () { return document.getElementById("some-id-in-source")!=null; },
function () { /* source is rendered and #some-id-in-source is available */ }
);
});
loadCss("file.css",function () {
$waitUntil(
function () { return element.offsetHeight>0; },
function () { /* css is applied on this element */ }
);
});
loadJs("file.js",function () {
$waitUntil(
function () { return typeof(SomeTypeInFileJs)!="undefined"; },
function () { /* SomeTypeInFileJs is ready to use */ }
);
});
הפונקציות loadCss, ajax ו-loadJs טוענות אסינכרונית קבצים ומודיעות כשהם נטענו. ניתן למצוא מימוש שלהן בכל ספריית JavaScript המכבדת את עצמה.
סוגי רכיבים נפוצים הם תמונה, קובץ css, קובץ javascript, פניית xmlhttp. לכל אחד מאלה יש אירוע בו אנו יודעים כי הוא נטען, אך עצם העובדה שהוא נטען לא אומרת שהוא התחיל להשפיע על העמוד שלנו.
לדוגמה:
- טעינה בזמן ריצה של קובץ css והשמתו על העמוד לא אומרת שמיידית הוא ישפיע על האלמנטים.
- קבלת html מתוך xmlhttp והשמתו על העמוד לא אומרת שמיידית כולו רונדר וניתן לגשת לרכיבים בו ללא בעיה
- טעינת קובץ javascript לעמוד לא אומרת שכל האובייקטים בו מוכנים לשימוש
לשם מצבי קצה אלו כתבתי פונקציה פשוטה בבסיסה, שממתינה עד אשר פעולה כלשהי בוצעה ורק אז מריצה קוד נוסף.
/// $waitUntil
/// waits until a certain function returns true and then executes a code. checks the function periodically
/// parameters
/// check - a function that should return false or true
/// onComplete - a function to execute when the check function returns true
/// delay - time in milliseconds, specifies the time period between each check. default value is 100
/// timeout - time in milliseconds, specifies how long to wait and check the check function before giving up
function $waitUntil(check,onComplete,delay,timeout) {
// if the check returns true, execute onComplete immediately
if (check()) {
onComplete();
return;
}
if (!delay) delay=100;
var timeoutPointer;
var intervalPointer=setInterval(function () {
if (!check()) return; // if check didn't return true, means we need another check in the next interval
// if the check returned true, means we're done here. clear the interval and the timeout and execute onComplete
clearInterval(intervalPointer);
if (timeoutPointer) clearTimeout(timeoutPointer);
onComplete();
},delay);
// if after timeout milliseconds function doesn't return true, abort
if (timeout) timeoutPointer=setTimeout(function () {
clearInterval(intervalPointer);
},timeout);
}
POC:
var globalVariable=0;
setTimeout(function () { globalVariable=1; },2000);
$waitUntil(
function () {
console.log("checking globalVariable="+globalVariable);
return globalVariable==1;
},
function () {
alert("done!");
}
);
בדוגמה אנו יכולים לראות שיש משתנה בשם globalVariableשמאותחל ל-0, ולאחר 2 שניות ערכו הופך ל-1 ידנית. הפונקציה $waitUntil מקבלת שתי פונקציות. האחת תחזיר true/false לפי בדיקה שתבצע, והשניה תבצע קוד שירוץ רק אחרי שהראשונה החזירה true.
* הפונקציה console.log שייכת ל-firebug/webkit/ie8 ולא נתמכת ב-ie6/7.
דוגמאות לשימושים יותר נפוצים:
ajax("url.htm",function (source) {
element.innerHTML=source;
$waitUntil(
function () { return document.getElementById("some-id-in-source")!=null; },
function () { /* source is rendered and #some-id-in-source is available */ }
);
});
loadCss("file.css",function () {
$waitUntil(
function () { return element.offsetHeight>0; },
function () { /* css is applied on this element */ }
);
});
loadJs("file.js",function () {
$waitUntil(
function () { return typeof(SomeTypeInFileJs)!="undefined"; },
function () { /* SomeTypeInFileJs is ready to use */ }
);
});
הפונקציות loadCss, ajax ו-loadJs טוענות אסינכרונית קבצים ומודיעות כשהם נטענו. ניתן למצוא מימוש שלהן בכל ספריית JavaScript המכבדת את עצמה.
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;קוד זה בא לענות על צורך של שינוי דינמי של src של תמונה. השימוש ב-onpropertychange נורה כאשר כל מאפיין שונה. בעזרת propertyName נבדוק האם שונה המאפיין src. אירוע זה עובד רק על IE.
this.attachEvent("onpropertychange",function (e) {
if (e.propertyName=="src") window.__fixPng(that);
});
מאחר וכל פונקציה מקבלת this משלה, ו-this שבתוך attachEvent הוא אינו האלמנט אלא החלון (למה?!), נאלץ לשמור אותו ברפרנס בשם that.
לבסוף, את הקוד נכליל בעזרת conditional comment ל-IE6 ומטה:
<!--[if lte IE 6]>
<link rel="stylesheet" href="fixpng.css" type="text/css"/>
<![endif]-->
מקווה שתמצאו קוד זה שימושי, אם כי לא לזמן רב מדי. אני מייחל מוות מהיר לשימוש ב-IE6 :)
דוגמה אונליין
הורדת הדוגמה
ASP.NET מרגילה מתכנתים לעבוד בצורה בזבזנית ומחפירה בכל הקשור ל-UI. כאחד שעוסק שנים רבות בפיתוח ל-Web בשלל טכנולוגיות, כל הזמן היה דגש כמה שפחות להטריד את השרת. לא רק ש-ASP.NET WebForms לא עושה את זה, היא אפילו מעודדת להטריד כמה שיותר. וזה פשוט רע.
כמה פעמים ראיתי קוד כמו
void btn_Click(object sender,EventArgs e) {
Response.Redirect("Page.aspx?Param=Value");
}
יש למישהו מושג איזה תהליך קורה כשלוחצים על הכפתור עד שמגיעים ללינק?אז תיאור קצרצר:
- לחיצה על הכפתור מפעילה רוטינות JS לבדוק שהטופס ואלידי בהקשר של אותו Validation Group של הכפתור (אם יש), ובנוסף שמה את הכפתור כ-EventTarget
- אם הכל כשורה - הטופס נשלח לשרת יחד עם כל ערכי הפקדים הקיימים על העמוד וכמובן ה-ViewState
- מגיעה בקשת HTTP לשרת, כל הרוטינות הרגילות, הפניות, יצירת HttpContext, הרצת כל ה-HttpModules - טעינת Session, Cache וכו'
- העמוד המכיל את הכפתור רץ: נוצר מופע מהמחלקה של העמוד
- כל שרשרת האירועים רצה, אתחול culture, קונטרולים, init, load, render וכמובן שרשרת האירועים של קונטרולים מקוננים בעמוד שלנו
- אירוע ה-Click ששמנו לכפתור, בין אם ב-aspx בעזרת asp:Button OnClick=btn_Click או בין אם בקוד בעזרת btn.Click+=btn_Click, מחווט ונרשם לכפתור
- השרת עכשיו בודק מי ה EventTarget שלו ומגלה שזה הכפתור. עכשיו הוא יורה את אירוע ה-Click של הכפתור ומגיע לנחלה - מבצע Response.Redirect.
נשמע הגיוני? לי ממש לא.
<a href="Page.aspx?Param=Value">click</a>זה הרבה יותר טוב.
<button onclick="location.href='Page.aspx?Param=Value';">click</button>
אז נכון, לא כולם עושים את זה. אבל לשם דוט נט מובילה אנשים שלא מכירים את התחום. ובמלוא מובן המילה - זה רע.
חושבים שזה לא משפיע? המשתמשים/לקוחות שלכם כבר יודעים לזהות אתר עם סיומת aspx כאתר שהולך להיות איטי לגלוש בו. כל האינטראקציה הלוך ושוב בין הקליינט לסרבר כל כך מיותרת ומכבידה על הגלישה עצמה. חוויית משתמש נוראית.
התדמית של ASP.NET כל כך יורדת בגלל היישום התמוה שמיקרוסופט עשו, גם בעיני משתמשים ובטח בעיני מתכנתים אחרים.
תחשבו על הסיטואציה הבאה: אתם מטיילים עם העכבר שלכם על המסך. בכל תזוזה של פיקסל - כל הפיקסלים על המסך מתרנדרים מחדש. הופכים להיות שחורים לעשירית שנייה ומקבלים צבע.
עכשיו תשוו את זה ל-ASP.NET WebForms :)








