原文出处: fastcodesign 译文出处:码农网 – 小峰 Froont,一家为web设计师制作工具的旧金山公司,用9个GIF格式动画来表现网页设计在过去25年的发展历程。如果你想知道为什么CSS这么重要,为什么Flash会逐渐淘汰,这些动画图或许能让你一窥究竟。 网页设计的单色时代,约1989年。想想Lynx和其他以Unix为基础的web浏览器,这些浏览器只会通过网络从这个终端到另一个终端一个字一个字地蹦出来。没有图形用户界面,只有一串字母和一个闪烁的光标。 现代化的开始,约1995年。第一个浏览器,例如Mosaic,允许设计人员在网站上显示图片,但是唯一的方法是将这些网页元素丢进不雅的表格布局中。由于这真的很难精确定位,所以设计师得琢磨地非常清楚如何写好表格嵌套表格再嵌套表格的代码以便正确显示内容。 幸运的是,JavaScript出现了,为设计师提供了很大的便捷。Flash让我们创建动态网站的设计更为简便:例如,弹出窗口和在页面上动态修改内容顺序。但是相较于本地HTML,JavaScript速度很慢。 尽管Flash也曾风靡一时,但是现在几乎已经被HTML5干掉了,但是在1996年,它代表了网页设计的一个新高度:能够让设计师按他们自己的方…
javascript实现一个简单的广告位, 发送数据请求使用 1×1 像素的透明 gif 图片, 网页中的广告统计,展现次数和点击次数
layout: post title : javascript实现一个简单的广告位 description : 一般广告位的实现都是一个独立的模块,哪里需要添加广告位,就把这个广告位插在哪里;而这个广告位通常都是由一个js链接导入的。 category : javascript
tags : [javascript, ad, advertisemen]
{% include JB/setup %}
一般广告位的实现都是一个独立的模块,哪里需要添加广告位,就把这个广告位插在哪里;而这个广告位通常都是由一个js链接导入的。
在这段广告位的js代码里,主要的功能点有:
- 代码采用闭包的方式,防止变量污染全局;
- 采用可配置项的方式进行调用:可以配置广告展示的开始时间、结束时间、广告位的宽和高;
jumeiForU.init({ "start":"2015/02/01 00:00", "end":"2016/01/01 00:00", "width":400, "height":400 });
- 每次刷新页面均采用随机数的方式进行广告的展示;如果想要进行顺序循环展示的话,那就得读写cookie了;
- 读取引用该js链接中所带的参数,比如下面的广告链接,我们能够获取到referer参数的值。
<script type="text/javascript" src="http://xxx.js?referer=wenzi"></script>
以下放出代码,大家可进行参考,欢迎批评建议:
;(function(){ var jumeiForU = { // 初始化 init : function(_config){ this.config = this.extend(this.config, _config); this.show(); }, // 广告展示及跳转链接 data : [ {'title':'九朵云祛斑霜', 'img':'http://p0.jmstatic.com/g/300x250/ht150122p854446t1.jpg', 'url':'http://www.jumeiglobal.com/deal/ht150122p854446t1.html?referer='}, {'title':'Guerisson奇迹马油24K金面膜贴', 'img':'http://p0.jmstatic.com/g/300x250/ht150122p1293256t1.jpg', 'url':'http://www.jumeiglobal.com/deal/ht150122p1293256t1.html?referer='}, {'title':'Its-skin晶钻蜗牛面膜', 'img':'http://p0.jmstatic.com/g/300x250/ht150122p818496t1.jpg', 'url':'http://www.jumeiglobal.com/deal/ht150122p818636t1.html?referer='}, {'title':'九朵云美白祛斑气垫BB霜', 'img':'http://p0.jmstatic.com/g/300x250/ht150122p1293254t1.jpg', 'url':'http://www.jumeiglobal.com/deal/ht150122p1293254t1.html?referer='}, {'title':'可莱丝NMF水库针剂睡眠面膜5片', 'img':'http://p0.jmstatic.com/g/300x250/ht150122p1312153t1.jpg', 'url':'http://www.jumeiglobal.com/deal/ht150122p1312153t1.html?referer='}, {'title':'猪皮面膜', 'img':'http://p0.jmstatic.com/g/300x250/ht150122p1254465t1.jpg', 'url':'http://www.jumeiglobal.com/deal/ht150122p1254465t1.html?referer='}, {'title':'奇迹马油精华套装', 'img':'http://p0.jmstatic.com/g/300x250/ht150122p1293257t1.jpg', 'url':'http://www.jumeiglobal.com/deal/ht150122p1293257t1.html?referer='}, {'title':'九朵云美白祛斑套组', 'img':'http://p0.jmstatic.com/g/300x250/ht150122p1293255t1.jpg', 'url':'http://www.jumeiglobal.com/deal/ht150122p1293255t1.html?referer='}, {'title':'晶钻蜗牛修护睡眠面膜', 'img':'http://p0.jmstatic.com/g/300x250/ht150122p818636t1.jpg', 'url':'http://www.jumeiglobal.com/deal/ht150122p818496t1.html?referer='}, {'title':'skin1004僵尸面膜', 'img':'http://p0.jmstatic.com/g/300x250/ht150122p1265333t1.jpg', 'url':'http://www.jumeiglobal.com/deal/ht150122p1265333t1.html?referer='} ], // 配置 config : { start : '2015/01/01 00:00', end : '2030/01/01 00:00', width : 300, height : 250 }, // 广告展示 show : function(){ var nowtime = (new Date()).getTime(), starttime = (new Date(this.config.start)).getTime(), endtime = (new Date(this.config.end)).getTime(), random = this.getRandom(), referer = this.getCurrentScript('referer'); if(nowtime>=starttime && nowtime<endtime){ document.write('<div style="position:relative; padding:0; margin:0; width:'+this.config.width+'px; height:'+this.config.height+'px"><a href="'+this.data[random].url+referer+'" target="_blank"><img src="'+this.data[random].img+'" alt="" style="width:'+this.config.width+'px; height:'+this.config.height+'px; border:0;" /></a></div>'); } }, // 返回当前需要展示的广告代号 getRandom : function(){ return Math.floor(Math.random()*this.data.length); }, extend : function(destination, source) { for (var property in source) { destination[property] = source[property]; } return destination; }, // 获取referer的值 getCurrentScript : function(name){ var i = 0, result = null, script, scripts, url, reg, r; // firefox支持currentScript属性 if( document.currentScript ){ script = document.currentScript } else{ // 正常情况下,在页面加载时,当前js文件的script标签始终是最后一个 scripts = document.getElementsByTagName( 'script' ) script = scripts[ scripts.length - 1 ] } url = script.hasAttribute ? script.src : script.getAttribute( 'src', 4 ); reg = new RegExp("(^|&|\\?)" + name + "=([^&]*)(&|$)", "i"); r = url.substr(1).match(reg); if (r !== null && r !==""){ result = decodeURIComponent(r[2]); } return result===""? null: result; } } jumeiForU.init(); })(window);
为什么通常在发送数据埋点请求的时候使用的是 1×1 像素的透明 gif 图片?
作用:工作中,用于前端监控,比如曝光等等,谷歌和百度的都是用的1×1 像素的透明 gif 图片;
why?
- 没有跨域问题,一般这种上报数据,代码要写通用的;(排除ajax)
- 不会阻塞页面加载,影响用户的体验,只要new Image对象就好了;(排除JS/CSS文件资源方式上报)
- 在所有图片中,体积最小;(比较PNG/JPG)
- 能够完成整个 HTTP 请求+响应(尽管不需要响应内容)
- 触发 GET 请求之后不需要获取和处理数据、服务器也不需要发送数据
- 跨域友好
- 执行过程无阻塞
- 相比 XMLHttpRequest 对象发送 GET 请求,性能上更好
- GIF的最低合法体积最小(最小的BMP文件需要74个字节,PNG需要67个字节,而合法的GIF,只需要43个字节)
参考资料:
SegmentFault 上的回答
Web beacon
Using a Beacon Image for GitHub, Website and Email Analytics
为什么前端监控要用 GIF 打点
英文术语叫:image beacon
在Google 的 Make the Web Faster 的 #Track web traffic in the background 中有提到。
主要应用于只需要向服务器发送数据(日志数据)的场合,且无需服务器有消息体回应。比如收集访问者的统计信息。
一般做法是服务器用一个1×1的gif图片来作为响应,当然这有点浪费服务器资源。因此用header来响应比较合适,目前比较合适的做法是服务器发送”204 No Content”,即“服务器成功处理了请求,但不需要返回任何实体内容”。
另外该脚本的位置一般放在页面最后以免阻塞页面渲染,并且一般情况下也不需要append到DOM中。通过它的onerror和onload事件来检测发送状态。
当我们访问带有Google Analyitcs追踪代码的页面时,页面中的GA追踪代码被执行,然后会向Google服务器发送一个1像素的图片请求。 (http://www.google-analytics.com/__utm.gif?params1=XX¶ms2=XX)并将所收集到的数据(比如浏览器版本啊)作为请求__utm.gif图片链接的变量一起发送回google服务器。然后经过google服务器的处理发布到我们的数据报告里
浏览器什么时候会发起网络请求,去加载一张图片?
浏览器在什么时候会去加载一张图片呢?当然是我们网页中有图片的时候。在平时的项目开发中,我们还常常会用图片进行日志上报,大概像下面这样:
var img = new Image(); img.src = 'http://上报地址?a=1'
新建一个Image,将其src赋值,浏览器便会发出一个网络请求。
再来考虑一下下面的代码:
var scriptEl = document.createElement('script'); scriptEl.src = 'http://xxxx.js';
上面的代码会发起网络请求么?
答案是:不会。只需要增加一行代码,将script插入到DOM树中,便会触发网络请求。
var scriptEl = document.createElement('script'); scriptEl.src = 'http://xxxx.js'; document.body.appendChild(scriptEl); // 这行代码会触发请求
什么时候会触发一次图片请求?
再来看下面的代码:
var imgStr = '<img src="xxx.png">'; var divEl = document.createElement('div'); divEl.innerHTML = imgStr;
上面的代码会触发网络请求么?答案是:会的。虽然divEl并没有被插入到DOM树中,但是网络请求依然会触发。这种表现一开始是让我有点意外的。于是乎,我开始了探索。
翻了一下html规范,发现了下面这段话:
地址:html.spec.whatwg.org/multipage/i…
大概的意思就是:
如果浏览器禁用了js脚本,那么浏览器可以立即请求图片,也可以根据需要加载图片。如果浏览器没有禁用js脚本,当img元素被创建,或者经历一些变化(src被赋值等) 时,浏览器必须立即进入一个update the image data的流程。在update the image data这个流程中,如果图片元素还没有内容,并且图片元素的src已被赋值,浏览器会立即发起请求去请求图片。
这样,就可以解释上述的现象了:
Case 1
var img = new Image(); img.src = 'http://上报地址?a=1' // Object.prototype.toString.call(img) === "[object HTMLImageElement]"
使用Image构造函数时,生成了一个HTMLImageElement
实例,也就是一个img元素,然后给这个img元素的src赋了值。很显然,我们使用了javascript。按照规范,浏览器必须立即发起网路请求,更新图片数据。
Case 2
var imgStr = '<img src="xxx.png">'; var divEl = document.createElement('div'); divEl.innerHTML = imgStr; // Object.prototype.toString.call(divEl.firstChild) === "[object HTMLImageElement]"
在使用了innerHTML后,其实我们也是生成了一个HTMLImageElement
实例。按照规范,浏览器也必须立即发起网络请求,更新图片数据。
总结一下:只要我们在代码中创建了一个img元素(HTMLImageElement实例),并且我们给这个img元素的src赋值了,那么浏览器就会发起网络请求,加载图片内容。
什么时候触发一次script请求?
我们再来规范里是怎么规定script标签的。
地址:html.spec.whatwg.org/multipage/s…
在script标签的相关规范里,我没有找到明确的说明在什么情况下需要发起网络请求加载资源。另外,我看到了上面红框里的这段话。概括一下,就是:
浏览器可以在设置script元素的src时候,就发起网络请求加载资源。但是如果最后,这个script元素没有插入DOM的话,网络请求就完全被浪费了。
可以理解为:浏览器可以在设置script元素的时候,自行考虑是否需要立即发起网络请求加载资源。然后浏览器在实现的时候,为了节约资源,并没有立即发起请求,而是选择了在插入DOM树后,才发起请求。
(这里不是很确定,但是没有找到更进一步的说明)
跳出规范来思考
不考虑规范,在平时的业务中,我们新建图片后,不管图片是否最终插入DOM树,都需要立即拿到图片的信息,比如canvas,比如通过图片的宽高进行页面排版等。因此,新建img元素后立即发起请求拿到图片数据,是合乎逻辑的。
对于script元素来说,也不存在需要单独操作的场景,为了节省资源,script插入DOM树后再发起网络请求,也是合乎逻辑的。
本文:javascript实现一个简单的广告位, 发送数据请求使用 1×1 像素的透明 gif 图片, 网页中的广告统计,展现次数和点击次数