JavaScript学习笔记(五)

五、浏览器

1.“window对象不但充当全局作用域,而且表示浏览器窗口”

'use strict';
// 获取浏览器窗口的内部宽度和高度(除去菜单栏、工具栏、边框等占位元素的显示网页的净宽高),但IE<=8不支持
console.log('window inner size: ' + window.innerWidth + ' x ' + window.innerHeight);

// 获取浏览器窗口的整个宽高
console.log('window outer size: ' + window.outerWidth + ' x ' + window.outerHeight);

// 结果
window inner size: 1120 x 522
window outer size: 1120 x 565

2.“navigator对象表示浏览器的信息”,常用属性:

navigator.appName // 浏览器名称
navigator.appVersion // 浏览器版本
navigator.language // 浏览器设置的语言
navigator.platform // 操作系统类型
navigator.userAgent // 浏览器设定的`User-Agent`字符串

注意“navigator的信息可以很容易地被用户修改,所以JavaScript读取的值不一定是正确的”。

3.“screen对象表示屏幕的信息”,常用属性:

screen.width // 屏幕宽度,以像素为单位
screen.height // 屏幕高度,以像素为单位
screen.colorDepth // 返回颜色位数,如8、16、24

'use strict';
console.log('Screen size = ' + screen.width + ' x ' + screen.height);
console.log('Screen color depth: ' + screen.colorDepth);

// 结果
Screen size = 1120 x 630
Screen color depth: 24

4.“location对象表示当前页面的URL信息”,对于一个完整的URL:http://www.example.com:8080/path/index.html?a=1&b=2#TOP

location.href // 'http://www.example.com:8080/path/index.html?a=1&b=2#TOP'
location.protocol; // 'http'
location.host; // 'www.example.com'
location.port; // '8080'
location.pathname; // '/path/index.html'
location.search; // '?a=1&b=2'
location.hash; // 'TOP'

若加载一个新页面,可以调用location.assign('新的URL');若重新加载当前页面,则调用location.reload()方法。

5.“document对象表示当前页面,documenttitle属性是从HTML文档中的<title>xxx</title>读取的,但也可以动态改变;用document对象提供的getElementById()getElementsByTagName()可以按ID获得一个DOM节点和按Tag名称获得一组DOM节点;document对象还有一个cookie属性,可以获取当前页面的Cookie:document.cookie”,为了解决引入的第三方Js存在恶意代码而获取当前页面Cookie中的用户登陆信息的问题,“服务器端在设置Cookie时,应该始终坚持使用httpOnly,设定了httpOnly的Cookie将不能被JavaScript读取”。

6.“history对象保存了浏览器的历史记录,JavaScript可以调用history对象的back()forward (),相当于用户点击了浏览器的“后退”或“前进”按钮”,但这是一个历史遗留问题,最好不用这个对象。

7.参考“HTML DOM教程”(https://www.runoob.com/htmldom/htmldom-tutorial.html)先学习一下HTML DOM相关内容。文档对象模型(Document Object Model)是HTML和XML文档的编程接口,HTML DOM 定义了访问和操作 HTML 文档的标准方法,DOM 以树结构表达 HTML 文档。根据 W3C 的 HTML DOM 标准,HTML 文档中的所有内容都是节点:

1)整个文档是一个文档节点

2)每个 HTML 元素是元素节点

3)HTML 元素内的文本是文本节点

4)每个 HTML 属性是属性节点

5)注释是注释节点
在这里插入图片描述

8.注意DOM树中节点的nodeName属性规定了节点的名称,该属性是只读的,元素节点的nodeName与标签名相同(且是大写的,如

节点的nodeName是P),属性节点的nodeName与属性名相同,文本节点的nodeName始终是#text,文档节点的nodeName始终是#document。

9.nodeValue属性规定节点的值,元素节点的nodeValue是undefined或null,属性节点的nodeValue是属性值,文本节点的nodeValue是文本本身。获取元素(节点)内容的最简单方法是使用innerHTML属性。

10.nodeType属性返回节点的类型且该属性是只读的,元素节点的nodeType是1,属性节点的是2,文本节点的是3,注视节点的是8,文档节点的是9。

11.注意查找带有相同类名的所有HTML元素应该用方法document.getElementsByClassName(“intro”);其返回包含 class=“intro” 的所有元素的列表(元素的class可能是"intro inner"等,该元素也符合要求)(该方法在IE5、6、7、8中无效)。

12.回到“廖雪峰的Python教程”,“由于ID在HTML文档中是唯一的,所以document.getElementById()可以直接定位唯一的一个DOM节点,document.getElementsByTagName()document.getElementsByClassName()总是返回一组DOM节点(列表或数组的形式,如果只有一个符合条件也是列表或数组形式),要精确地选择DOM,可以先定位父节点,再从父节点开始选择,以缩小范围”。

13.练习对于如下Html代码,选择出指定条件的节点:

<!-- HTML结构 -->
<div id="test-div">
<div class="c-red">
    <p id="test-p">JavaScript</p>
    <p>Java</p>
  </div>
  <div class="c-red c-green">
    <p>Python</p>
    <p>Ruby</p>
    <p>Swift</p>
  </div>
  <div class="c-green">
    <p>Scheme</p>
    <p>Haskell</p>
  </div>
</div>

js代码如下,js变量得到的是元素节点<p id="test-p">JavaScript</p>;arr变量对应的document.getElementsByClassName('c-red c-green')[0]得到的是元素节点 <div class="c-red c-green">,有两种方法得到该节点的所有子节点;haskell变量对应的document.getElementsByClassName('c-green')[1]得到的是元素节点<div class="c-green">document.getElementsByClassName('c-green')[0]对应的应该是元素节点<div class="c-red c-green">

'use strict';
// 选择<p>JavaScript</p>:
var js = document.getElementById('test-p');

// 选择<p>Python</p>,<p>Ruby</p>,<p>Swift</p>:
var arr = document.getElementsByClassName('c-red c-green')[0].children;
// var arr = document.getElementsByClassName('c-red c-green')[0].getElementsByTagName('p');

// 选择<p>Haskell</p>:
var haskell = document.getElementsByClassName('c-green')[1].lastElementChild;

// 测试:
if (!js || js.innerText !== 'JavaScript') {
    alert('选择JavaScript失败!');
} else if (!arr || arr.length !== 3 || !arr[0] || !arr[1] || !arr[2] || arr[0].innerText !== 'Python' || arr[1].innerText !== 'Ruby' || arr[2].innerText !== 'Swift') {
    console.log('选择Python,Ruby,Swift失败!');
} else if (!haskell || haskell.innerText !== 'Haskell') {
    console.log('选择Haskell失败!');
} else {
    console.log('测试通过!');
}

另外还可以使用querySelector()querySelectorAll()获取节点,前者返回匹配指定CSS选择器元素的第一个子元素(不代表只能获取带有class属性的节点),后者返回所有匹配元素。了解querySelector方法(https://www.runoob.com/jsref/met-element-queryselector.html),了解有关css选择器的内容(https://www.runoob.com/cssref/css-selectors.html)(待学习)(选择器element>element,示例:div>p,表示选择所有(直接)父级是

元素的

元素)

14.如果通过修改innerHTML属性来修改节点的内容(文本或者子节点),需要注意是否需要写入HTML,如果写入的字符串是通过网络拿到的,要注意对字符编码来避免XSS攻击;如果修改innerTexttextContent属性,就可以自动对字符串进行HTML编码,保证无法设置任何HTML标签,两者的区别在于读取属性时innerText不返回隐藏元素的文本,而textContent返回所有文本(注意IE<9不支持textContent)(以上属性被赋予的值都是字符串)。“DOM节点的style属性对应所有的CSS,可以直接获取或设置(赋予的都是字符串,如test节点修改颜色:test.style.color='#ff0000')。因为CSS允许font-size这样的名称,但它并非JavaScript有效的属性名,所以需要在JavaScript中改写为驼峰式命名fontSize”。

15.练习对于如下Html结构:

<!-- HTML结构 -->
<div id="test-div">
  <p id="test-js">javascript</p>
  <p>Java</p>
</div>

获取指定节点并修改:

'use strict';

// 获取<p>javascript</p>节点:
var js = document.getElementById('test-js');

// 修改文本为JavaScript:
// TODO:
js.innerHTML = 'JavaScript';
//js.innerText = 'JavaScript';
//js.textContent = 'JavaScript';

// 修改CSS为: color: #ff0000, font-weight: bold
// TODO:
js.style.color = '#ff0000';
js.style.fontWeight = 'bold';
// 测试:
if (js && js.parentNode && js.parentNode.id === 'test-div' && js.id === 'test-js') {
    if (js.innerText === 'JavaScript') {
        if (js.style && js.style.fontWeight === 'bold' && (js.style.color === 'red' || js.style.color === '#ff0000' || js.style.color === '#f00' || js.style.color === 'rgb(255, 0, 0)')) {
            console.log('测试通过!');
        } else {
            console.log('CSS样式测试失败!');
        }
    } else {
        console.log('文本测试失败!');
    }
} else {
    console.log('节点测试失败!');
}

16.“如果插入的js节点已经存在于当前的文档树,这个节点首先会从原先的位置删除,再插入到新的位置”,在使用appendChile方法或insertBefore方法前,需要获取父节点,再为其插入子节点。“使用insertBefore重点是要拿到一个‘参考子节点’的引用。很多时候,需要循环一个父节点的所有子节点,可以通过迭代父节点的children属性实现”:

var
    i, c,
    list = document.getElementById('list');
for (i = 0; i < list.children.length; i++) {
    c = list.children[i]; // 拿到第i个子节点
}

17.练习对于如下html代码:

<!-- HTML结构 -->
<ol id="test-list">
    <li class="lang">Scheme</li>
    <li class="lang">JavaScript</li>
    <li class="lang">Python</li>
    <li class="lang">Ruby</li>
    <li class="lang">Haskell</li>
</ol>

按字符串顺序重新排序DOM节点:

'use strict';
// 方法一,直接对子节点进行(傻瓜式)排序:
var list = document.getElementById('test-list');
for (var i = 0; i < list.children.length; ++i) {
    for (var j = i+1; j < list.children.length; ++j) {
        if (list.children[i].innerText > list.children[j].innerText) {
            list.insertBefore(list.children[j], list.children[i]);
        }
    }
}

// 方法二,把子节点的文本抠出来作为数组排序,然后对子节点一个一个赋值
var list = document.getElementById('test-list');
var i, arr = [];
for (i = 0; i < list.children.length; ++i) {
    arr.push(list.children[i].innerHTML);
}
arr.sort();
for (i = 0; i < list.children.length; ++i) {
    list.children[i].innerHTML = arr[i];
}
// 测试:
;(function () {
    var
        arr, i,
        t = document.getElementById('test-list');
    if (t && t.children && t.children.length === 5) {
        arr = [];
        for (i=0; i<t.children.length; i++) {
            arr.push(t.children[i].innerText);
        }
        if (arr.toString() === ['Haskell', 'JavaScript', 'Python', 'Ruby', 'Scheme'].toString()) {
            console.log('测试通过!');
        }
        else {
            console.log('测试失败: ' + arr.toString());
        }
    }
    else {
        console.log('测试失败!');
    }
})();

18.“删除多个节点时,要注意children属性时刻都在变化”,练习对于如下html代码:

<!-- HTML结构 -->
<ul id="test-list">
    <li>JavaScript</li>
    <li>Swift</li>
    <li>HTML</li>
    <li>ANSI C</li>
    <li>CSS</li>
    <li>DirectX</li>
</ul>

把与Web开发技术不相关的节点删掉:

'use strict';
var 
    i,
    needs = ['JavaScript', 'HTML', 'CSS'],
    list = document.getElementById('test-list'),
    children = list.children;
for (i = 0; i < children.length; ++i) {
    if(needs.indexOf(children[i].innerHTML) === -1)
        list.removeChild(children[i]);
}

// 测试:
;(function () {
    var
        arr, i,
        t = document.getElementById('test-list');
    if (t && t.children && t.children.length === 3) {
        arr = [];
        for (i = 0; i < t.children.length; i ++) {
            arr.push(t.children[i].innerText);
        }
        if (arr.toString() === ['JavaScript', 'HTML', 'CSS'].toString()) {
            console.log('测试通过!');
        }
        else {
            console.log('测试失败: ' + arr.toString());
        }
    }
    else {
        console.log('测试失败!');
    }
})();

19.如果获得了文本框(<input type="text">)、口令框(<input type="password">)、下拉框(<select>)和隐藏文本(<input type="hidden">)的元素节点,可以通过节点的属性value获得他们的值或修改值;如果是单选框(<input type="radio">)和复选框(<input type="checkbox">),“value属性返回的永远是HTML预设的值,而需要获得的实际是用户是否‘勾上了’选项,所以应该用属性checked判断,也可以设置checkedtruefalse”。

20.在表单中,没有name属性的<input>节点的数据不会被提交。

21.练习利用JavaScript检查用户注册信息是否正确,在以下情况不满足时报错并阻止提交表单:

  • 1)用户名必须是3-10位英文字母或数字;
  • 2)口令必须是6-20位;
  • 3)两次输入口令必须一致。
<!-- HTML结构 -->
<form id="test-register" action="#" target="_blank" onsubmit="return checkRegisterForm()">
    <p id="test-error" style="color:red"></p>
    <p>
        用户名: <input type="text" id="username" name="username">
    </p>
    <p>
        口令: <input type="password" id="password" name="password">
    </p>
    <p>
        重复口令: <input type="password" id="password-2">
    </p>
    <p>
        <button type="submit">提交</button> <button type="reset">重置</button>
    </p>
</form>
'use strict';
var checkRegisterForm = function () {
    var
        userRe = /[a-zA-Z0-9]{3,10}/,
        passRe = /.{6, 20}/,
        form = document.getElementById('test-register'),
        username = document.getElementById('username'),
        password = document.getElementById('password'),
        repassword = document.getElementById('password-2');
    if (userRe.test(username.value)) {
        if (passRe.test(password.value)) {
            if (password.value === repassword.value) {
                return true;
            }else {
                window.alert('两次输入的密码应该一致!');
                return false;
            }
        }else {
            window.alert('密码的长度应该大于6小于20!');
            return false;
        }
    }else {
        window.alert('用户名必须是3-10位英文字母或数字!');
        return false;
    }
}
// 测试:
;(function () {
    window.testFormHandler = checkRegisterForm;
    var form = document.getElementById('test-register');
    if (form.dispatchEvent) {
        var event = new Event('submit', {
    		bubbles: true,
    		cancelable: true
  		});
        form.dispatchEvent(event);
    } else {
        form.fireEvent('onsubmit');
    }
})();

22.java中求string的长度用函数string(),javascript中求string的长度用属性length。

23.“任何时候,JavaScript代码都不可能同时有多于1个线程在执行”,执行多任务一般都是异步操作,而为了确保异步操作可以结束,需要设置在执行异步操作前设置一个回调函数,异步操作执行完成后自动执行回调函数然后获得响应。

24.Ajax意味着用JavaScript执行异步网络请求。“默认情况下,JavaScript在发送AJAX请求时,URL和当前页面必须同域,即协议、域名、端口号完全相同,该情况下url是相对路径”。

25.理解ajax跨域请求中的jsonp,参考链接

26.“拥有"src"这个属性的标签都拥有跨域的能力,比如<script><img><iframe>”。

27.“跨源域资源共享( CORS )机制允许 Web 应用服务器进行跨源访问控制,从而使跨源数据传输得以安全进行。”学习链接(不管做前端还是后端这篇文章都需要看看,建议先复习一下计算机网络)

28.“如果在js的线程中出现耗时操作,就容易堵塞后续代码的执行。因此在js中如果碰到一些可能需要耗费一些时间的操作,像setTimeout,ajax的回调函数(称其为异步操作)等,js会将其放入一个代办任务队列taskqueue中,当js按顺序执行完其他同步的,不耗时的操作之后,会去依次执行taskqueue队列中的任务”,参考链接

29.参考b站视频理解promise对象:promise对象用于表示一个异步操作的最终完成(成功或失败)及所返回的结果值。用promise对象可以降低代码的耦合度,参考链接:15分钟理解js中的promise对象

30.对于代码promise对象.then(参数1).catch(参数2),参数1和2可以是函数也可以是promise对象(异步任务),如果是函数的话,参数1是函数resolve,参数2是函数reject

对于代码:

var p = new Promise(function (resolve, reject) {
    log('start new Promise...');
    resolve(123);
});

函数resolve在异步操作执行成功时执行,函数reject在异步操作执行失败时执行。

31.js中的setTimeOut函数

//IE9 及其更早版本不支持第三个及之后的参数
var alertFunc = function(a,b){console.log(a,b)};
setTimeout(alertFunc, 2000, "Runoob", "Google");

//另外一种写法
setTimeout(function(){ alertFunc("Runoob", "Google"); }, 2000);

第三个及之后的参数是setTimeout()函数的可选参数,作为参数传给 setTimeout() 方法里面的匿名函数或者调用的函数。

32.“除了串行执行若干异步任务外,Promise还可以并行执行异步任务”:

var p1 = new Promise(function (resolve, reject) {
    setTimeout(resolve, 500, 'P1'); // 'P1'作为参数传递给函数resolve
});
var p2 = new Promise(function (resolve, reject) {
    setTimeout(resolve, 600, 'P2'); // 'P2'作为参数传递给函数resolve
});

// 同时执行p1和p2,并在它们都完成后执行then:
Promise.all([p1, p2]).then(function (results) {
    console.log(results); // 获得一个Array: ['P1', 'P2']
});

“有时多个异步任务是为了容错,比如同时向两个URL读取用户的个人信息,只需要获得先返回的结果即可。这种情况下,用Promise.race()实现”,因为p1等待的时间少于p2等待的时间,所以最后输出的结果是’P1’,但p2仍在继续执行,只是执行结果将被丢弃:

var p1 = new Promise(function (resolve, reject) {
    setTimeout(resolve, 500, 'P1');
});
var p2 = new Promise(function (resolve, reject) {
    setTimeout(resolve, 600, 'P2');
});
Promise.race([p1, p2]).then(function (result) {
    console.log(result); // 'P1'
});

33.“如果浏览器支持Canvas,它将忽略<canvas>内部的HTML,如果浏览器不支持Canvas,它将显示<canvas>内部的HTML;Canvas的坐标以左上角为原点,水平向右为X轴,垂直向下为Y轴,以像素为单位,所以每个点都是非负整数。”

34.练习题画k线图,一开始不明白是啥,百度了解了一下基本原理,阳线一般是红色的,阴线是绿色的(敲代码还能学到股市有关知识,绝了)
在这里插入图片描述

绘制k线图的函数接收的参数data是一个数组,其每个元素都是一个对象,查了一下Path2D咋用,参考api链接。答案代码如下:

var
    canvas = document.getElementById('stock-canvas'), // 获取画布
    width = canvas.width,
    height = canvas.height,
    ctx = canvas.getContext('2d'), // 一个CanvasRenderingContext2D对象,用于绘图
    colWidth = canvas.width / 30 / 1.5, // k线图中矩形的宽(阳线和阴线的宽)
    start,  // 矩形的中心线的起始位置
    spacing,    // 矩形的中心线的间隔
    max = data.map(x => x.high).sort()[data.length - 1],    // 30天中的最高价
    min = data.map(x => x.low).sort()[0],   // 30天中的最低价
    unitLen = canvas.height / (max - min),  // 单位价格区间长度对应的长度(y坐标)
    bottom = canvas.height; // canvas的底部
            
    // 限制矩形的宽度和高度
    if (colWidth > 10) {
        colWidth = 10;
    }
    if (unitLen > 1) {
        bottom = 1 / unitLen * bottom;  // 使图像整体往上移动
        unitLen = 1;
    }

    start = colWidth / 2;
    spacing = colWidth * 1.5;
    console.log(JSON.stringify(data[0])); // {"date":"20150602","open":4844.7,"close":4910.53,"high":4911.57,"low":4797.55,"vol":62374809900,"change":1.69}
    // 序列化并输出data数组的第一个对象,看一下具体数据组成

    // 擦除(0,0)位置大小为weigth x height的矩形,即把该区域变透明
    ctx.clearRect(0, 0, width, height);
    // ctx.fillText('Test Canvas', 10, 10);
        
    for (let i = 0; i < data.length; i++) {
        // 绘制每个矩形的中心线
        var path = new Path2D();
        let coord = start + spacing * i;    // 每个矩形的中心线的x坐标  
        // 将一个新的子路径的起始点移动到对应坐标
        path.moveTo(coord, bottom - (data[i].low - min) * unitLen);
        //console.log(bottom - (data[i].low - min) * unitLen);
        // 使用直线连接子路径的终点到对应坐标
        path.lineTo(coord, bottom - (data[i].high - min) * unitLen);
        //console.log(bottom - (data[i].low - min) * unitLen);
        ctx.strokeStyle = 'black'; // 线的颜色和样式
        ctx.stroke(path);  // 绘制矩形的中心线

        // 绘制矩形,注意细节,canvas的y轴是向下的
        let higher, lower;
        if (data[i].open < data[i].close) { // 如果开盘价小于收盘价就是红线(阳线)
            ctx.fillStyle = 'red';
            higher = data[i].close;
            lower = data[i].open;
        } else { // 如果开盘价大于收盘价就是绿线(阴线)
            ctx.fillStyle = 'green';
            higher = data[i].open;
            lower = data[i].close;
        }
        
        // 绘制一个填充了内容的矩形,这个矩形的开始点(左上点)在(coord - colWidth / 2, bottom - (higher - min) * unitLen) ,它的宽度和高度分别由colWidth和(higher - lower) * unitLen)确定,填充样式由当前的fillStyle决定
        ctx.fillRect(coord - colWidth / 2, bottom - (higher - min) * unitLen, colWidth, (higher - lower) * unitLen);
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值