图片 2

【jQuery UI 1.8 The User Interface Library for jQuery】.学习笔记.10.Button 和 Autocomplete控件

Posted by

H5 Crash 研究

2016/05/31 · HTML5 ·
Crash

原文出处:
小胡子哥(@Barret李靖)   

我们知道,支撑页面在 webview 上良好运转的前提是具备一个高效并且稳定的
webview
容器,而容器的高效稳定不仅仅由容器提供方来保障,也需要容器使用者遵守一些基本准则,否则就有可能出现页面
Crash
的情况,这些准则是什么?什么样的上层代码会引起容器异常退出?这是本文需要阐述的内容。

Button
,可以使用 <button> <input> <a>。 <input>
中的不同类型,submit , radio , checkbox 。还能加上 icon ,split button

H5 Crash 问题概况

下图是 H5 Crash 的大致流程图:

图片 1

由于前端没办法捕捉到页面 Crash 的状态和堆栈,但是 H5
页面上发生的错误会传递到 Java 和更底层的 Native
直到容器异常退出,在退出的那一刻,容器会将堆栈写入到日志中,当下次打开容器时(也可能是定时上报)就会上报这些堆栈信息。

Autoconplete
为校准 文本
<input>提供了一个文本选择的菜单。当浏览者开始在<input>中输入时,会自动匹配输入的字符,显示建议。
允许通过箭头按键导航,Enter键选中,Esc键关闭菜单。当箭头键被用来导航时,每个建议都会出现在<input>中。如果Esc用来关闭导航,<input>的
value 会回复到用户输入的状态。

H5 Crash 原因初探

测试代码 仓库地址:

git clone ; cd demo;

1
2
git clone https://github.com/barretlee/h5crash.git;
cd demo;

注意: 代码需要在 Webview 容器中测试,PC 浏览器下不会出现异常。

H5 Crash 的原因不太明显,但是从经验上判断和摸索,大致归类为以下三种:

1. 内存问题

  • 测试方法:使用闭包,不断增加内存量,看看增加到哪个区间大小, webview
    容器会出现异常
  • 测试地址:(微信、微博或者其他客户端打开该页面的用户,可以点进去测试下,选择
    100M 内存,不出意外,你的客户端会闪退。)

XHTML

<script> var Closure = function() { var _cache = []; var cache
= 0; var add = function(size) { cache += size; size = size * 1024 *
1024; _cache.push(new Array(size).join(‘x’)); refresh(); }; var refresh
= function() { r.innerHTML = ‘内存消耗: ‘ + cache + ‘M’; }; return {
cache: cache + ‘M’, add: add, refresh: refresh } }; var closure =
Closure(); </script> <button onclick=”closure.add(1)”>增加
1M 内存消耗</button> <button onclick=”closure.add(10)”>增加
10M 内存消耗</button> <button onclick=”closure.add(20)”>增加
20M 内存消耗</button> <button onclick=”closure.add(50)”>增加
50M 内存消耗</button> <button
onclick=”closure.add(100)”>增加 100M 内存消耗</button> <div
id=”r”>内存消耗:0 M</div>

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
<script>
var Closure = function() {
  var _cache = [];
  var cache = 0;
  var add = function(size) {
    cache += size;
    size = size * 1024 * 1024;
    _cache.push(new Array(size).join(‘x’));
    refresh();
  };
  var refresh = function() {
    r.innerHTML = ‘内存消耗: ‘ + cache + ‘M’;
  };
  return {
    cache: cache + ‘M’,
    add: add,
    refresh: refresh
  }
};
var closure = Closure();
</script>
 
<button onclick="closure.add(1)">增加 1M 内存消耗</button>
<button onclick="closure.add(10)">增加 10M 内存消耗</button>
<button onclick="closure.add(20)">增加 20M 内存消耗</button>
<button onclick="closure.add(50)">增加 50M 内存消耗</button>
<button onclick="closure.add(100)">增加 100M 内存消耗</button>
 
<div id="r">内存消耗:0 M</div>

存在的干扰:这种测试存在比较多的干扰,比如设备类型、系统类型(iOS/Android)、和设备内存运行状态等。

2. Layers 数问题

Layers 数的获取比较麻烦,Chrome Driver
没有提供该数据的接口,目前也没有比较好的办法拿到这个数据。

  • 测试方法:通过不同的方式创建层,观察页面的 Crash 情况
  • 测试地址:

XHTML

<style>.transform { transform: translateZ(0); } .animation {
width:100px; height:100px; background:red; position:relative;
animation:move 5s infinite; } @keyframes move { from {left:0px;} to
{left:200px;} } </style> <script> var Layer = function() {
function getType() { return
document.querySelector(‘input:checked’).value; }; return { createOne:
function(index) { var div = document.createElement(‘div’);
div.appendChild(document.createTextNode(index)); switch(getType()) {
case ‘opacity’: div.style.cssText = “opacity:” + (index / 1000); break;
case ‘transform’: div.className = ‘transform’; break; case ‘animation’:
div.className = ‘animation’; break; case ‘zindex’: div.style.cssText =
“position:relative; z-index:” + index; break; }
document.body.appendChild(div); }, create: function(num) {
[].slice.call(document.querySelectorAll(‘div’)).forEach(function(item)
{ item.parentNode && item.parentNode.removeChild(item); }); while(num–)
{ this.createOne(num); } } } }; var layer = Layer(); </script>
<strong>层类型: </strong> <ul>
<li><label><input type=”radio” checked name=”type”
value=”opacity”> <span>通过 opacity
创建层</span></label></li>
<li><label><input type=”radio” name=”type”
value=”transform”> <span>通过 transforms
创建层</span></label></li>
<li><label><input type=”radio” name=”type”
value=”animation”> <span>通过 animation
创建层</span></label></li>
<li><label><input type=”radio” name=”type”
value=”zindex”>
<span>通过绝对定位分层</span></label></li>
</ul> <button onclick=”layer.create(1)”>创建 1
个层</button> <button onclick=”layer.create(10)”>创建 10
个层</button> <button onclick=”layer.create(20)”>创建 20
个层</button> <button onclick=”layer.create(50)”>创建 50
个层</button> <button onclick=”layer.create(100)”>创建 100
个层</button> <button onclick=”layer.create(200)”>创建 200
个层</button> <button onclick=”layer.create(500)”>创建 500
个层</button> <button onclick=”layer.create(1000)”>创建 1000
个层</button> <button onclick=”layer.create(2000)”>创建 2000
个层</button> <button onclick=”layer.create(5000)”>创建 5000
个层</button> <button onclick=”layer.create(10000)”>创建
10000 个层</button>

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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
<style>.transform {
  transform: translateZ(0);
}
.animation {
  width:100px;
  height:100px;
  background:red;
  position:relative;
  animation:move 5s infinite;
}
 
@keyframes move {
  from {left:0px;}
  to {left:200px;}
}
</style>
<script>
var Layer = function() {
  function getType() {
    return document.querySelector(‘input:checked’).value;
  };
  return {
    createOne: function(index) {
      var div = document.createElement(‘div’);
      div.appendChild(document.createTextNode(index));
      switch(getType()) {
        case ‘opacity’:
          div.style.cssText = "opacity:" + (index / 1000);
          break;
        case  ‘transform’:
          div.className = ‘transform’;
          break;
        case  ‘animation’:
          div.className = ‘animation’;
          break;
        case  ‘zindex’:
          div.style.cssText = "position:relative; z-index:" + index;
          break;
      }
      document.body.appendChild(div);
    },
    create: function(num) {
      [].slice.call(document.querySelectorAll(‘div’)).forEach(function(item) {
        item.parentNode && item.parentNode.removeChild(item);
      });
      while(num–) {
        this.createOne(num);
      }
    }
  }
};
var layer = Layer();
</script>
 
<strong>层类型: </strong>
<ul>
  <li><label><input type="radio" checked name="type" value="opacity"> <span>通过 opacity 创建层</span></label></li>
  <li><label><input type="radio" name="type" value="transform"> <span>通过 transforms 创建层</span></label></li>
  <li><label><input type="radio" name="type" value="animation"> <span>通过 animation 创建层</span></label></li>
  <li><label><input type="radio" name="type" value="zindex"> <span>通过绝对定位分层</span></label></li>
</ul>
 
<button onclick="layer.create(1)">创建 1 个层</button>
<button onclick="layer.create(10)">创建 10 个层</button>
<button onclick="layer.create(20)">创建 20 个层</button>
<button onclick="layer.create(50)">创建 50 个层</button>
<button onclick="layer.create(100)">创建 100 个层</button>
<button onclick="layer.create(200)">创建 200 个层</button>
<button onclick="layer.create(500)">创建 500 个层</button>
<button onclick="layer.create(1000)">创建 1000 个层</button>
<button onclick="layer.create(2000)">创建 2000 个层</button>
<button onclick="layer.create(5000)">创建 5000 个层</button>
<button onclick="layer.create(10000)">创建 10000 个层</button>
  • 实际上,创建多个层,也是对内存的巨大消耗,页面 Crash
    可能还是因为内存消耗过大

3. 并发过多问题

  • 测试方法:尝试并发发出多种不同的请求(Fetch请求、XHR
    请求、Script/CSS 资源请求),观察页面 Crash 情况
  • 测试地址:

XHTML

<script> var Request = function() { function getType() { return
document.querySelector(‘input:checked’).value; }; function getResource()
{ var type = getType(); var resource = { fetch: ‘/’, xhr: ‘/’, script:
‘//g.alicdn.com/sd/data_sufei/1.5.1/aplus/index.js’, css:
‘//g.alicdn.com/kg/global-util/1.0.3/index-min.css’ }; return
resource[type]; }; return { emitOne: function() { var url =
getResource() + “?_t=” + (new Date * 1 + Math.random());
switch(getType()) { case ‘fetch’: return fetch(‘/’); case ‘xhr’:
with(new XMLHttpRequest) { open(‘GET’, url); send(); } return; case
‘script’: var s = document.createElement(‘script’); s.src = url;
document.body.appendChild(s); return; case ‘css’: var s =
document.createElement(‘link’); s.href = url;
document.body.appendChild(s); } }, emit: function(num) {
[].slice.call(document.querySelectorAll(‘script,link’)).forEach(function(item)
{ item.parentNode && item.parentNode.removeChild(item); }); while(num–)
{ this.emitOne(); } } } }; var request = Request(); </script>
<strong>请求类型: </strong> <ul>
<li><label><input type=”radio” checked name=”type”
value=”fetch”> <span>使用 Fetch
发送请求</span></label></li>
<li><label><input type=”radio” name=”type”
value=”xhr”> <span>使用 XHR
发送请求</span></label></li>
<li><label><input type=”radio” name=”type”
value=”script”>
<span>并发请求脚本资源</span></label></li>
<li><label><input type=”radio” name=”type”
value=”css”>
<span>并发请求样式资源</span></label></li>
</ul> <button onclick=”request.emit(1)”>并发 1
个请求</button> <button onclick=”request.emit(10)”>并发 10
个请求</button> <button onclick=”request.emit(20)”>并发 20
个请求</button> <button onclick=”request.emit(50)”>并发 50
个请求</button> <button onclick=”request.emit(100)”>并发 100
个请求</button> <button onclick=”request.emit(500)”>并发 500
个请求</button> <button onclick=”request.emit(1000)”>并发
1000 个请求</button>

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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
<script>
var Request = function() {
  function getType() {
    return document.querySelector(‘input:checked’).value;
  };
  function getResource() {
    var type = getType();
    var resource = {
      fetch: ‘/’,
      xhr: ‘/’,
      script: ‘//g.alicdn.com/sd/data_sufei/1.5.1/aplus/index.js’,
      css: ‘//g.alicdn.com/kg/global-util/1.0.3/index-min.css’
    };
    return resource[type];
  };
  return {
    emitOne: function() {
      var url = getResource() + "?_t=" + (new Date * 1 + Math.random());
      switch(getType()) {
        case ‘fetch’:
          return fetch(‘/’);
        case ‘xhr’:
          with(new XMLHttpRequest) {
            open(‘GET’, url);
            send();
          }
          return;
        case ‘script’:
          var s = document.createElement(‘script’);
          s.src = url;
          document.body.appendChild(s);
          return;
        case ‘css’:
          var s = document.createElement(‘link’);
          s.href = url;
          document.body.appendChild(s);
      }
    },
    emit: function(num) {
      [].slice.call(document.querySelectorAll(‘script,link’)).forEach(function(item) {
        item.parentNode && item.parentNode.removeChild(item);
      });
      while(num–) {
        this.emitOne();
      }
    }
  }
};
var request = Request();
</script>
 
<strong>请求类型: </strong>
<ul>
  <li><label><input type="radio" checked name="type" value="fetch"> <span>使用 Fetch 发送请求</span></label></li>
  <li><label><input type="radio" name="type" value="xhr"> <span>使用 XHR 发送请求</span></label></li>
  <li><label><input type="radio" name="type" value="script"> <span>并发请求脚本资源</span></label></li>
  <li><label><input type="radio" name="type" value="css"> <span>并发请求样式资源</span></label></li>
</ul>
 
<button onclick="request.emit(1)">并发 1 个请求</button>
<button onclick="request.emit(10)">并发 10 个请求</button>
<button onclick="request.emit(20)">并发 20 个请求</button>
<button onclick="request.emit(50)">并发 50 个请求</button>
<button onclick="request.emit(100)">并发 100 个请求</button>
<button onclick="request.emit(500)">并发 500 个请求</button>
<button onclick="request.emit(1000)">并发 1000 个请求</button>
  • 存在的干扰:设备的种类、设备的 CPU 使用情况和网络状况等。
  • 标准button的安装启用
  • 配置选项
  • 添加icon
  • button
    事件
  • button组
  • button方法
  • 使用本地数据源的autocomplete
  • autocomplete的配置选项
  • autocomplete事件
  • autocomplete独特的方法
  • 使用远程数据源的autocomplete
  • 在autocomplete的建议菜单中使用HTML

H5 Crash 测试结果

测试结果:

  • 通过 opacity、animation、positon 等方式创建层,即便是 1w
    个,页面也没有明显变化;但是使用 transform 创建 2k~5k
    个层,页面会卡顿几秒后立即闪退;
  • 内存是条红线,测试发现,一次性消耗 20M
    的内存,会导致客户端立即闪退;
  • 并发请求也是存在响应问题的,Fetch API 和 CSS Resource 并发 1k
    请求没有出现问题,但是 XHR 和 Script Resource
    请求,问题特别明显,虽然没有导致页面闪退,但是页面已经进入了假死状态。

以上临界值还可以继续精确。

1.1 标准安装启用

当使用<a>或
<button> 元素创建 button
,一个<span>元素会被自动创建,并嵌入其中。这个<span>会包含button
的 label text.

1 <a
href=””
id=”myButton”>A link
button</a>2 <button
id=”myButton2″> button
</button>3 <input
id=”myButton3″ value=”An
input button”>4
$(function(){ 5 $(“#myButton”).button(); 6
$(“#myButton2”).button(); 7
$(“#myButton3”).button(); 8
});

<input>
元素不允许包含子元素,所以<span>不会在使用<input>创建button的时候出现。

<a>元素通过制定
href 属性,会简单地将浏览者送往新的页面或anchor。

使用
<button> 元素同上,只是不需要添加
href属性。使用<input>元素也很简单,它使用 value属性设置 button
的文本。

小结

本文主要是对 H5 Crash
做了一个预研,测试可能存在诸多误差,测试方法也需要改进,不过沿着这些的思路考究会比较容易找到结论。

后续会给出比较有意义的边界数据以及探测工具。

 

1 赞 3 收藏
评论

图片 2

1.2 配置选项

Option Default Value Used to…
disabled false 禁用button实例
icons {primary:null,secondary:null} 设置button实例的icons
label value属性或下层元素的文本 设置button实例的text
text true 当使用icon-only时,隐藏button的 text

label选项配置的值会覆盖<a>元素的文本。

1.3 button的 icons

button在大多数情况下顶多有另个icon。当一个<a>或<button>元素被用来创建button,我们可以使用icons配置选项来为其指定一个或两个icons。

1
var buttonOpts={ 2 icons:{ 3
primary:”ui-icon-disk”, 4
secondary:”ui-icon-triangle-1-s”
5 }, 6
text:false 7 };

icons属性接受一个对象,它包含最多两个键,primary和secondary。这些选项的值可以是
jquery.ui.theme.css 文件中的任何 ui-icon- classes。icons使用<span>元素添加到控件中。

1.4 input 的 icons

尽管子元素<span>可以用来显示指定的icon,但是我们不能为<input>元素添加子元素。我们可以使用额外的容器,包含<span>元素和一些自定义的CSS,简单地为<input>添加我们自己的icons。

1 <style>
2
.iconic-input { display:inline-block; position:relative;} 3 .ui-icon
{ z-index:2;} 4 .iconic-input input
{ border:none; margin:0;} 5 </style>
6 </script>
7 <div class=”iconic-input ui-button-text-icons
ui-state-default ui-corner-all”>
8 <span class=”ui-button-icon-primary ui-icon
ui-icon-disk”></span>
9 <input id=”myButton”
type=”button” value=”Input icons” class=”ui-button-text”>10 <span
class=”ui-button-icon-secondary ui-icon
ui-icon-triangle-1-s”></span>11 </div>

在较老版本的IE中不支持display:inline-block样式。为了防止button占满它的容器,我们需要将其浮动,设置期望宽度。

两端的icons 没有
hover states,这是因为控件为<input>应用了必须的
classnames,没有给我们自定义的容器。我们可以为icon添加必要的行为。

1
$(“#myButton”).button().hover(function(){ 2
$(this).parent().addClass(“ui-state-hover”); 3
},function(){ 4 $(this).parent().removeClass(“ui-state-hover”);
5 });

为icon添加悬停效果。多数情况下,使用<a>
和<button>元素创建 button 会更简单,更有效率。

1.5 button 的事件

使用<a>元素创建的button,会通过href跳转。在
<form>中使用 <button>或<input>元素,创建带有 type
属性的设置,会提交表单数据。

button控件只暴露了一个事件
create 事件。当 button 实例初始化创建后被触发。

我们也许想要收集一些注册信息,并使用
button 发送信息到服务器。

1 <form
method=”post”
action=”aa.php”>
2 <label for=”name”>Name:
3 <input
type=”text”
id=”name” name=”name”>
4 </label>
5 <label for=”email”>Email:
6 <input
type=”text”
id=”email” name=”email”>
7 </label>
8 <button type=”submit”
id=”myButton”>Register</button>
9 </form>10 11
$(function(){ 12 $(“#myButton”).button().click(function(e){
13 e.preventDefault(); 14 var
form=$(“form”), 15 formData={ 16
name:form.find(“#name”).val(), 17
email:form.find(“#email”).val() 18 };
19 console.log(formData); 20
$.post(“aa.php”,formData,function(){ 21
$(“#myButton”).button(“option”,”disabled”,true); 22
form.find(“label”).remove(); 23
$(“<label />”).text(“Thanks for
registering!”).prependTo(form); 24 })
25 }); 26
});

1.6 button组

button控件也可以用来结合
radio buttons 和 checkboxes 。button是 jQuery UI
控件中,独一无二的,有两个方法的控件,它还有一个 buttonset()
方法,用来创建一组由 radio buttons 和 chekboxes 组成的button。

1.6.1 Checkbox buttonsets

1 <div
id=”buttons”>
2 <h2>Programming Languages</h2>
3 <p>Select
all languages you know:</p>
4 <label for=”js”>JavaScript</label>
5 <input id=”js”
type=”checkbox”>
6 <label for=”py”>Python</label>
7 <input id=”py”
type=”checkbox”>
8 <label for=”cSharp”>C#</label>
9 <input id=”cSharp”
type=”checkbox”>10 <label
for=”jv”>Java</label>11 <input
id=”jv”
type=”checkbox”>12 </div>13
$(function(){ 14 $(“#buttons”).buttonset(); 15
});

当一个checkbox被选中,它会被应用
selected
状态。在HTML5中,使用关联的<label>元素构建form时,不允许使用button
控件。

1.6.2 Radio buttonsets

与checkbox
的区别仅仅在行为上,所有的<input>都必须有 name 属性。

1 <div
id=”buttons”>
2 <h2>Programming Languages</h2>
3 <p>Select
all languages you know:</p>
4 <label for=”js”>JavaScript</label>
5 <input id=”js”
type=”radio” name=”lang”>
6 <label for=”py”>Python</label>
7 <input id=”py”
type=”radio” name=”lang”>
8 <label for=”cSharp”>C#</label>
9 <input id=”cSharp”
type=”radio” name=”lang”>10 <label
for=”jv”>Java</label>11 <input
id=”jv”
type=”radio” name=”lang”>12 </div>

1.7 Button 的方法

除了 destroy ,
disable, enable ,widget , option 这些公共的方法,button
控件只暴露了一个自定义的方法,refresh方法。它用来以代码的方式改变checkbox
和 raido 的状态。

1 <div
id=”buttons”>
2 <h2>Programming Languages</h2>
3 <p>Select
all languages you know:</p>
4 <label for=”js”>JavaScript</label>
5 <input id=”js”
type=”checkbox”>
6 <label for=”py”>Python</label>
7 <input id=”py”
type=”checkbox”>
8 <label for=”cSharp”>C#</label>
9 <input id=”cSharp”
type=”checkbox”>10 <label
for=”jv”>Java</label>11 <input
id=”jv”
type=”checkbox”>12 </div>13 <br>14 <button
type=”button”
id=”select”>Select
all</button>15 <button
type=”button”
id=”deselect”>Deselect all</button>16 17
$(function(){ 18 $(“#buttons”).buttonset(); 19
$(“#select”).button().click(function(){ 20
$(“#buttons”).find(“input”).attr(“checked”,true).button(“refresh”);
21 }); 22
$(“#deselect”).button().click(function(){ 23
$(“#buttons”).find(“input”).attr(“checked”,false).button(“refresh”);
24 }); 25
});

当 Select all
button被点击,我们设置 cheked 属性为 true 。这会检查底层并且隐藏的check
boxes,单不会对作为button显示的<label>元素做任何事情。更新这些button
的状态,让他们显示被选中,需要调用 refresh 方法。

相关文章

Leave a Reply

电子邮件地址不会被公开。 必填项已用*标注