angularjs-placeholder submit a form on IE

使用angularjs-placeholder可以在老舊IE上,支援placeholder這個屬性,但是底層還是去實作foucsblur這兩個event,並且用替換value的方式,達到模擬placeholder的效果。在一般使用上沒什麼太大問題,但是假設今天有一個formsubmit的時候,會把placeholder的預設值也一起送出,這時就必須在submit之前,就將預設值清除,或者在取值得時候,暫時將欄位值清除,之後在還原。

使用placeholder.ensure取值:

ensure接受兩個參數:

1
2
3
4
5
6
7

/**
* @param {jQlite} $elem 可以是form或者為多個elements
* @param {function} callback 實際取值的地方
*/


placeholder.ensure( $elem, callback );

callback裡面取得值後,可使用record.back來回復placeholder的預設值。

1
2
3
4
5
6
7
8
9
10
11
12
app.controller( "formController", [ "placeholder", "$element", function( placeholder, $form ){

placeholder.ensure( $form , function( record ){

var $textarea = $form.find("textarea");
alert( $textarea );

//復原
record.back();
});

});

若要直接submit可直接呼叫ensure,不須使用callback。

1
placeholder.ensure( $form )

如果使用有支援placeholder的瀏覽器,還是會有ensure可以使用,但是將不會改變任何屬性,純粹只是個包裝過後的function。

Fixed a placeholder feature on IE (angularjs)

Placeholder要支援老舊的IE7~IE9,使用jQuery也都有一堆plugin可以使用了。如果要在angularjs上使用,但是又不想載入jQuery,這時候可以使用angularjs-placeholder module。

使用angularjs-placeholder

載入angular module:

1
<script type="text/javascript" src="../src/angularjs-placeholder.js"></script>

定義需要使用到的module,和初始化ng-app:

1
2
3
4
<script type="text/javascript">
var app = angular.module( "demoApp", ["html5.placeholder"] );
angular.bootstrap( document, [app.name] );
</script>

實作angularjs-placeholder遇到的小問題

在實作angularjs-placeholder module時,在IE7發現一個小問題,無法取得placeholder的值,但是在有的jQuery情況,都會幫忙處理掉了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* input default: <input placeholder="Please,enter your name!" type="text" />
*/


//use native

var attr = input.getAttribute("placeholder");

// attr == null
console.log( attr );

//use jqlite
var $elem = angular.element( input );

// attr == null
console.log( $elem.attr( "placeholder" ) );

所以如果要在IE7取得placeholder,這時候native有提供另一個api可以使用。

1
2
3
4
5
6
var node = input.getAttributeNode( "placeholder" );

var attr = node?node.nodeValue:node;

//Please,enter your name!
console.log( attr );

Javascript prototype and this記憶體用量

在javascript中,使用function建立一個object時,用prototype來配置屬性,被建立的object會指向同一個prototype object,而使用this建立的,則會是獨立的屬性

Example:

定義TomMary二個人。

1
2
3
4
5
6
7
function Tom(){

}

function Mary(){

}

使用prototype定義John的父母,分別為TomMary

1
2
3
4
5
6
John.prototype = {
parents:{
dad:new Tom(),
mom:new Mary()
}
};

this的方式定義John的父母。

1
2
3
4
5
6
7
function John(){

this.parents = {
dad:new Tom(),
mom:new Mary()
};
}

然後用thisprototype的方式,各自建立1000000

1
2
3
4
5
var people = [];

for( var i = 0; i < 1000000 ; i++ ){
people.push( new John() );
}

結果如下:

使用prototype的方式
使用this的方式

this使用的記憶體為72.5m,而prototype的記憶體是25m,由此可知,使用prototype來建立多個object,會節省很多記憶體。


完整Prototype Example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46

<!DOCTYPE HTML>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<h1>use "prototype"</h1>
<button id="create">create children</button>
<div id="count">0</div>

<script type="text/javascript">
function Tom(){

}

function Mary(){

}

function John(){

}

John.prototype = {
parents:{
dad:new Tom(),
mom:new Mary()
}
};

var people = [],countElem = document.querySelector("#count");

document.querySelector("#create").addEventListener( "click", function(){

for( var i = 0; i < 1000000 ; i++ ){
people.push( new John() );
}

countElem.innerHTML = people.length;
});

</script>
</body>
</html>

完整This Example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49

<!DOCTYPE HTML>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<h1>use "this"</h1>
<button id="create">create children</button>
<div id="count">0</div>

<script type="text/javascript">

//define dad
function Tom(){

}

//define mom
function Mary(){

}

//define child
function John(){

this.parents = {
dad:new Tom(),
mom:new Mary()
};
}


var people = [],countElem = document.querySelector("#count");

//create children
document.querySelector("#create").addEventListener( "click", function(){

for( var i = 0; i < 1000000 ; i++ ){
people.push( new John() );
}

countElem.innerHTML = people.length;
});

</script>
</body>
</html>

Javascript Prototype的概念

javascript的prototypethis,是剛接觸javascript的人比較難理解的。在javascript裡,假設定義一個一個Bird function時,可使用同個prototype建立不同物件,也就所有經由Bird function產生的物件,都會擁有相同的protoype的屬性。

Example:

定義一隻鳥,這種鳥的名稱叫做Sparrow

1
2
3
4
5
6
7
function Bird(){
//do something
}

Bird.prototype = {
name:"Sparrow"
};

接著建立兩個變數,分別為Tom的寵物

1
2
var bird = new Bird();
var petOfTom = new Bird();

Tom把他的寵物稱為Raven,而這種的名稱,重新命名為Black Sparrow

1
2
3
petOfTom.name = "Raven";

Bird.prototype.name = "Black Sparrow";

最後你就會發現一個有趣現象,即使這種的名稱重新命名了,Tom的寵物仍然會以Tom命名的方式顯示

1
2
3
4
5
//name of bird:Black Sparrow 
console.log( "name of bird:" + bird.name );

//Tom's pet called him: Raven.
console.log( "Tom's pet called him: " + petOfTom.name + "." );

也就是說也就是說,當prototype的name改變時,其他經由Bird new出來的object name,都會跟著修改但是被new出來的object,一旦屬性被修改,不管之後prototype如何更動,則object的屬性會依照你所指定的修改


完整範例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40

<!DOCTYPE HTML>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>

<script type="text/javascript">

function Bird(){

}

Bird.prototype = {
name:"Sparrow"
};

var bird = new Bird();
var petOfTom = new Bird();

console.log( "name of bird:" + bird.name );

//rename
petOfTom.name = "Raven";

//rename
Bird.prototype.name = "Black Sparrow";

console.log( "rename..." );

console.log( "name of bird:" + bird.name );

console.log( "Tom's pet called him: " + petOfTom.name + "." );

</script>

</body>
</html>

Running AngularJS App on IE7

IE7需要額外在body宣告宣告id和ng-app屬性:

1
<body id="ng-app" ng-app="myapp">

盡量使用class、attribute的寫法,例如:

1
2
<div class="ng-view"></div>
<div ng-view></div>

若要在IE7使用angular自訂定義的tag,而不採用上述方式,如下:

1
<ng-view></ng-view>

需要先執行以下script,可參考官方說明

1
2
3
4
5
6
7
8
9
10
11
12
<!--[if lte IE 8]>
<script>
document.createElement('ng-include');
document.createElement('ng-pluralize');
document.createElement('ng-view');

// Optionally these for CSS
document.createElement('ng:include');
document.createElement('ng:pluralize');
document.createElement('ng:view');
</script>
<![endif]-->

在IE8、IE7使用angular.fromJson,需額外include script,http://bestiejs.github.com/json3/

1
2
3
<!--[if lte IE 8]>
<script src="/static/js/libs/json3.min.js"></script>
<![endif]-->

weinre

weinre是一個用於瀏覽器的遠端除錯工具,運作方式與jsconsole相同,都是透過include一隻外部js script,來與server溝通傳遞資料。但是jsconsole只提供一些很基本功能,如果要能像chrome一樣,查看dom element、network等功能,可以使用weinre

使用時機:

使用android、ios等手持裝置的browser,或者對IE除錯的時候,都很適合。

weinre原理:

weinre基本原理是,建立一個console page,然後經由weinre server,與development page(你要除錯的page)互相傳遞資料溝通。( 溝通方式採用long polling)

時序圖如下:

 
透過node.js安裝:
sudo npm install -g weinre
操作流程:

開啟weinre server:

weinre --httpPort 8081 --boundHost localhost

打開瀏覽器輸入http://localhost:8081/

然後在你想要除錯的頁面加上以下script:

<script src="http://localhost:8081/target/target-script-min.js#anonymous"></script>

例如:

<!DOCTYPE HTML>                                                                                  
<html lang="en">                                                                                 
<head>                                                                                           
    <meta charset="UTF-8">                                                                       
    <title></title>                                                                              
    <script src="http://localhost:8081/target/target-script-min.js#anonymous"></script>       
</head>                        
<body>                                                                                           
    <div>weinre test</div>                                                                       
    <script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script> 
</body>                                                                                          
</html>                                                                                          

開啟http://localhost:8081/client/#anonymous,就可以開始除錯了。

jquery uploader

使用ajax upload還滿常使用到,但是又不需要一些複雜的功能,所以就寫了一個簡單的jquery uploader

需求如下:
  1. 將form經用ajax post出去。
  2. 選擇要post的欄位包括file。
支援:
  1. firefox
  2. chrome
  3. IE10+

使用ajax submit整個form:

//opts.success(result):上傳成功
//opts.progress:處理進度
//opts.fail:上傳失敗

$("form").uploadForm( opts );

uploadForm會自動抓取form的action連結,目前此plugin只能一次抓取一個form upload。

使用ajax submit部份欄位:

//url:要上傳的目標連結

$( "input[name='file'],input[name='id']" ).upload( url, opts );

如果要支援IE10以下瀏覽器,現在已經另外實作iframe uploader,目前可以支援uploadForm這個功能,但是僅支援success這個參數。

github

Backbone.js emulateHTTP and emulateJSON

Backbone.emulateHTTP值為true時,會模擬restful的動作,將delete和put(update) method,全部都用post method代替,然後添加X-HTTP-Method-Override的header和_method的參數。

Example如下:

先建立一個Book Model。

Backbone.emulateHTTP = true;
Backbone.emulateJSON = true;
var Book = Backbone.Model.extend({
  urlRoot:"/books"     
});                      
var book = new Book({id:32});

執行save時,會發送request到server。(在backbone的model id有值的時候,save會是update功能,反之則為create。)

                                                                                                                                                                                            
book.save({name:"Tom"}); 

發送request到server端的header,會添加以下這段:

X-HTTP-Method-Override:PUT

Backbone.emulateJSON為true時,request的content-type會設定為application/x-www-form-urlencoded,傳遞到server參數如下:

model={"id":32,"name":"Tom"}&_method=PUT

Backbone.emulateJSON為false時,request的content-type會設定為application/json,傳遞到server參數如下:

{"id":32,"name":"Tom"}

所以當request的content-type不是application/json時,使用emulateHTTP,會多傳一個_method的參數到server端,可供server辨識。

javascript event偵錯-Visual Event 2

Visual Event會透過將javascript語法,注入到頁面的方式,取得每個element所綁定的事件。如果是常常include很多library時,或者寫一些比較複雜ui,可透Visual Event檢視綁定element上的event正不正確,方便除錯。

執行流程如下:

將以下script,加入到書籤的網址欄位:

javascript:(function()%20{var%20url%20=%20'//www.sprymedia.co.uk/VisualEvent/VisualEvent_Loader.js';if(%20typeof%20VisualEvent!='undefined'%20)%20{if%20(%20VisualEvent.instance%20!==%20null%20)%20{VisualEvent.close();}else%20{new%20VisualEvent();}}else%20{var%20n=document.createElement('script');n.setAttribute('language','JavaScript');n.setAttribute('src',url+'?rand='+new%20Date().getTime());document.body.appendChild(n);}})();

然後在選擇你要測試的頁面,執行剛剛加入的書籤,或將以上script貼到網址列,再按enter,就能偵測到綁定的event:

目前support的library如下:

  • DOM 0 events
  • jQuery 1.2+
  • YUI 2
  • MooTools 1.2+
  • Prototype 1.6+
  • Glow
  • ExtJS 4.0.x

注入的方式其實就是透過書籤、網址列可以執行javascript特性,當你點擊書籤時,就會執行script,將程式load當前頁面。在網址列執行script範例,如下:

//將以下程式碼貼到網址列按enter,就可以執行js語法
javascript:alert(3);

至於偵測event的實作,大致上是將每個element上的attribute掃過,還有針對不同library綁定event方式去處理,最後將ui呈現畫面上。