《 jQuery 基础教程》第4版习题解答
最近在学《jQuery 基础教程》(第4版),Google 了一圈没找到完整的习题解答,所以放在这里作个参考。
Chapter 2 选择器
1.给位于嵌套列表第二个层次的所有 <li>
元素添加 special 类
$('li').children().find('li').addClass('special');
2.给位于表格第三列的所有单元格添加 year 类
$('tr').find('td:eq(2)').addClass('year');
3.为表格中包含文本 Tragedy 的第一行添加 special 类
$('td:contains(\"Tragedy\")').first().parent().addClass('special');
4.(挑战)选择包含链接(<a>
元素)的所有列表项(<li>
元素),为每个选中的列表项的同辈列表项元素添加 afterlink 类
$('a').parent().nextAll().addBack().addClass('afterlink');
5.(挑战)为与 .pdf 链接最接近的祖先元素 <ul>
添加 tragedy 类
$('a[href$=\"pdf\"]').closest('ul').addClass('tragedy');
Chapter 3 事件
1.在 Charles Dickens 被单击时,应用 selected 样式
$('div.author').click(function () {
if ($(this).hasClass('selected')) {
$(this).removeClass('selected');
} else {
$(this).addClass('selected');
}
});
2.双击章节标题(h3
)时,切换本章文字的可见性
$('h3.chapter-title').dblclick(function () {
$(this).parent().addClass('hidden');
});
3.当用户按下向右方向键时,切换到下一个 body 类,右方向键的键码是39
var bodyClasses = [
'default',
'narrow',
'large'
];
$(document).keyup(function (event) {
if (event.which === 39) {
for (i = 0; i < 2; i++) {
if ($('body').hasClass(bodyClasses[i])) {
$('body').removeClass().addClass(bodyClasses[i + 1]);
break;
}
}
if (i == 2) {
setBodyClass(bodyClasses[0]);
}
}
});
上面这种写法比较“笨拙“。这里有更灵巧的写法,妙用switch
case
语句。
4.(挑战)使用 console.log()
函数记录在段落中移动的鼠标的坐标位置
$('p').mousemove(function (event) {
console.log(\"eventXY: (\" + event.pageX + \",\" + event.pageY + \")\");
});
5.(挑战)使用 .mousedown()
和 .mouseup()
跟踪页面中的鼠标事件。如果鼠标按键在按下的地方被释放,则为所有段落添加 hidden 类。如果是在按下的地方之下被释放,删除所有段落的 hidden 类
var mouseDownX, mouseDownY;
$(document).mousedown(function (event) {
mouseDownX = event.pageX;
mouseDownY = event.pageY;
});
$(document).mouseup(function (event) {
if (event.pageX === mouseDownX && event.pageY === mouseDownY) {
$('p').addClass('hidden');
} else if (event.pageY > mouseDownY) {
$('p').removeClass('hidden');
}
});
Chapter 4 样式与动画
1.修改样式表,一开始先页面内容,当页面加载后,慢慢地淡入内容
$('#container').hide().fadeIn('slow'); // 其实不需要修改样式表
2.在鼠标悬停到段落上面时,给段落应用黄色背景
var oldBackgroundColor;
$('div p').hover(
function () {
// hover 中使用 animate() 有问题???
/*
$(this).animate({
backgroundColor: 'yellow'
}, 'fast');
*/
oldBackgroundColor = $(this).css('backgroundColor');
$(this).css({
backgroundColor: 'yellow'
});
}, function () {
/*
//var backgroundColor = $('p').css('backgroundColor')
$(this).animate({
bacgroundColor: 'transparent'
}, 'fast')
*/
$(this).css({
backgroundColor: oldBackgroundColor
});
});
3.单击标题 <h2>
时将不透明度变为 25%,同时增加 20px 的左外边距,这两个效果完成后把讲话文本变成 50% 不透明度
$('h2').click(function () {
$(this).animate({
opacity: .25,
paddingLeft: '+=20px'
}, 'slow', function () {
// 不同要素的动画默认是同步进行的
// 使用回调函数,保证上面的动画完成后再设置 div.speech 动画
$('div.speech').animate({
opacity: .5
}, 'slow')
})
});
4.按下方向键时,使样式转换器 #switcher
向相应的方向平滑移动20像素。
注意:这里使用 .filter(':not(:animated)')
可以防止按键过快导致动画“堆积”。
$(window).keydown(function (event) {
var switcher = $('#switcher');
switch (event.which) {
case 37:
switcher.css('position', 'relative').filter(':not(:animated)').animate({
left: '-=20px'
}, 'fast');
break;
case 38:
switcher.css('position', 'relative').filter(':not(:animated)').animate({
top: '-=20px'
}, 'fast');
break;
case 39:
switcher.css('position', 'relative').filter(':not(:animated)').animate({
left: '+=20px'
}, 'fast');
break;
case 40:
switcher.css('position', 'relative').filter(':not(:animated)').animate({
top: '+=20px'
}, 'fast');
break;
default:
}
});
Chapter 5 操作 DOM
1.修改添加 back to top 的代码,以便这些链接只从第四段后面才开始出现。
$('div.chapter p').each(function (index) {
if (index >= 3) {
$(this).after($('<a href=\"#top\">back to top</a>'));
}
});
2.在单击 back to top 链接时,为每个链接后面添加一个新段落,其中包含 you were here 字样
// 给提示文字添加了一个类,以便在需要的时候清除
$('a[href*=\"#top\"]').click(function (event) {
var clickedLink = event.target;
$('.scrollLocationTip').remove();
$('<p class=\"scrollLocationTip\">you were here</p>').insertAfter(clickedLink);
event.stopPropagation();
});
// 不在这些链接上点击时,将提示文字清除
$('body').click(function () {
$('.scrollLocationTip').remove();
});
3.单击作者的名字时,把文本改为粗体(通过添加一个标签,而不是操作类或 CSS 属性)
4.(挑战)在随后单击粗体作者名字时,之前添加的 <b>
元素(也就是在文本与正常文本之间切换)
// 题干中用的是 <b> 标签,实际上为了语义与样式分离,用 <strong> 更合适
$('#f-author').click(function () {
if ($(this).children('strong').length == 0) {
// wrapInner(),不改变外部的 DOM 结构
// 判断 length 的目的是,防止多次 wrapInner()
$(this).wrapInner('<strong></strong>');
} else {
$(this).children('strong').contents().unwrap('<strong></strong>');
}
});
这两个练习还有另外一种写法,在两种状态之间“切换”可以使用 toggle()
方法来实现。
$('#f-author').toggle(
function () {
$(this).wrapAll('<b></b>');
},
function () {
$(this).unwrap()
}
);
5.(挑战)为正文中的每个段落添加一个 inhabitants 类,但不能调用 addClass()
方法。确保不影响现有的类
// $('.chapter p').addClass('inhabitants');
// 用 $(this).className += 'inhabitants 为什么不行???
$('.chapter p').each(function () {
if (!$(this).hasClass('inhabitants')) {
$(this).attr({
class: $(this).attr('class') + ' inhabitants'
});
}
});
Chapter 6 通过 Ajax 发送数据
1.页面加载后,把 exercises-content.html
的主体内容(body
)提取到页面的内容区域
// .load() 中的选择器为什么不能是 body???
$('#dictionary').load('exercises-content.html .letter');
2.不要一次显示整个文档,为左侧的字母列表创建“提示条”,当用户鼠标放到字母上时从 exercises-content.html
中加载与该字母有关的内容
/*
$('.letter a').hover(function () {
$('#dictionary').load('exercises-content.html #' + $(this).closest('.letter').attr('id'));
}, function () {
$('#dictionary').html('');
});
*/
$('.letter').hover(function () {
$('#dictionary').load('exercises-content.html #' + $(this).attr('id'));
}, function () {
$('#dictionary').html('');
});
3.为上面的页面加载添加错误处理功能,在页面的内容区显示错误消息。修改脚本,请求 does-not-exist.html 而不是 exercises-content.html,以测试错误处理功能。
$('.letter').hover(function () {
$('#dictionary').load('does-not-exist.html #' + $(this).attr('id'), function (response, status, xhr) {
if (status === 'error') {
$('#dictionary').html('错误:' + xhr.status).append(xhr.responseText);
} else {
$('#dictonary').html(xhr.responseText);
}
});
}, function () {
$('#dictionary').html('');
});
4.页面加载后,向 GitHub 发出一个 JSONP 请求,取得某用户的代码库列表。把每个代码库的名称和 URL 插入到页面的内容区。例如 jQuery 项目代码库的 URL 是 https://api.github.com/users/jquery/repos
var repoURL = 'https://api.github.com/users/jquery/repos';
$.getJSON(repoURL + '?callback=?', function (response) {
var html = '';
$.each(response.data, function (entryIndex, entry) {
html += '<div class=\"entry\">';
//给内容加上 term、part 类,是为了直接使用样式表中的样式
html += '<div class=\"repoName term\">' + entry.name + '</div>';
html += '<div class=\"repoURL part\">' + '<a href=\"' + entry.url + '\">' + entry.url + '</a>' + '</div>';
html += '</div>';
});
$('#dictionary').html(html);
});
Chapter 7 使用插件
1.把幻灯片的切换周期延长到 1.5 秒,把动画的效果修改为下一张幻灯片淡入之前,前一张幻灯片淡出。
$books.cycle({
timeout: 2000,
speed: 1500,
pause: true,
fx: 'fade', // 添加这一行
before: function () {
$('#slider').slider('value', $('#books li').index(this));
}
});
2.设置名为 cyclePaused 的 cookie,将它的有效期设置为30天
$.cookie('cyclePaused', null, {
expires: 7
});
3.限制书名区域,每次 resize 只允许以10像素为单位
$books.find('.title').resizable({
handles: 's',
grid: [10, 10] // 添加这一行 | 好像不起作用
});
4.修改 slider 的动画,让滑块平滑地移动到下一个位置。
$('<div id=\"slider\"></div>').slider({
min: 0,
max: $('#books li').length - 1,
animate: true, // 添加这一行
slide: function (event, ui) {
$books.cycle(ui.value);
}
}).appendTo($controls);
5.幻灯片播放完最后一张后停止。停止播放时,也禁用相应的按钮和滑动条。
$books.cycle({
timeout: 2000,
speed: 1500,
pause: true,
fx: 'fade',
nowrap: true, // 添加这一行
before: function () {
$('#slider').slider('value', $('#books li').index(this));
}
});
7.修改 mobile.html 中的 HTML 代码,让列表视图根据书名的第一个字母分隔开来。
可以在 html 中添加几个带有 data-role=\"list-divider\"
属性的标签:
<div data-role=\"content\">
<ul data-role=\"listview\" data-inset=\"true\" data-filter=\"true\">
<li data-role=\"list-divider\">D</li>
<li><a href=\"#drupal-7\">Drupal 7 Development by Example</a></li>
<li data-role=\"list-divider\">J</li>
<li><a href=\"#jq-game\">jQuery Game Development Essentials</a></li>
<li><a href=\"#jqmobile-cookbook\">jQuery Mobile Cookbook</a></li>
<li><a href=\"#jquery-designers\">jQuery for Designers</a></li>
<li><a href=\"#jquery-hotshot\">jQuery Hotshot</a></li>
<li><a href=\"#jqui-cookbook\">jQuery UI Cookbook</a></li>
<li data-role=\"list-divider\">M</li>
<li><a href=\"#mobile-apps\">Creating Mobile Apps with jQuery Mobile</a></li>
<li data-role=\"list-divider\">W</li>
<li><a href=\"wp-mobile-apps.html\">WordPress Mobile Applications with PhoneGap</a></li>
</ul>
</div>
Chapter 8 开发插件
1.创建 .slideFadeIn()
和 .slideFadeOut()
的插件方法,把不透明度动画方法 .fadeIn()
和 .fadeOut()
以及高度动画方法 .slideDown()
和 .slideUp()
结合起来。
(function ($) {
$.fn.slideFadeIn = function () {
return this.each(function () {
// 用 fadeIn() 效果不明显
$(this).slideDown('slow').fadeTo('slow', 1.0);
});
};
$.fn.slideFadeOut = function () {
return this.each(function () {
// 用 fadeOut() 效果不明显
$(this).fadeTo('slow', 0.5).slideUp('slow');
});
};
})(jQuery);
// 练习1 测试代码
$('#container h1').click(function () {
$('#inventory').slideFadeOut();
$('#inventory').slideFadeIn();
});
2.扩展 .shadow()
方法的可定制能力,让插件用户可以指定元素副本的 z 轴索引。为提示条控件添加一个 .isOpen()
子方法,在提示条正在显示的时候返回 true
,而在其他时候返回 false
。
(function ($) {
$.fn.shadow = function (opts) {
var options = $.extend($.fn.shadow.defaults, opts);
return this.each(function () {
var $originalElement = $(this);
for (var i = 0; i < options.copies; i++) {
var offset = options.copyOffset(i);
$originalElement
.clone()
.css({
position: 'absolute',
left: $originalElement.offset().left + offset.x,
top: $originalElement.offset().top + offset.y,
margin: 0,
// ========== 练习2
zIndex: options.zIndex,
// ==========
opacity: options.opacity
})
.appendTo('body');
}
});
}
})(jQuery);
$.fn.shadow.defaults = {
copies: 5,
opacity: 0.1,
copyOffset: function (index) {
return {
x: index,
y: index
};
},
// ========== 练习2添加
zIndex: -1
// ==========
};
(function ($) {
$.widget('ljq.tooltip', {
options: {
offsetX: 10,
offsetY: 10,
content: function () {
return $(this).data('tooltip-text');
}
},
_create: function () {
// ========== 练习2
this.isTooltipOpen = false;
// ==========
this._tooltipDiv = $('<div></div>')
.addClass('ljq-tooltip-text ' + 'ui-widget ui-state-highlight ui-corner-all')
.hide().appendTo('body');
this.element
.addClass('ljq-tooltip-trigger')
.on('mouseenter.ljq-tooltip', $.proxy(this._open, this))
.on('mouseleave.ljq-tooltip', $.proxy(this._close, this));
},
destroy: function () {
this._tooltipDiv.remove();
// this.element 不就是 $(this) 吗?
this.element
.removeClass('ljq-tooltip-trigger')
.off('.ljq-tooltip');
$.Widget.prototype.destroy.apply(this, arguments);
},
// ========== 练习2
isOpen: function () {
return this.isTooltipOpen;
},
// ==========
_open: function () {
if (!this.options.disabled) {
var elementOffset = this.element.offset();
this._tooltipDiv.css({
position: 'absolute',
left: elementOffset.left + this.options.offsetX,
top: elementOffset.top + this.element.height() + this.options.offsetY
}).text(this.options.content.call(this.element[0]));
this._tooltipDiv.show();
// ========== 练习2
this.isTooltipOpen = true;
// ==========
this._trigger('open');
}
},
open: function () {
this._open();
},
_close: function () {
this._tooltipDiv.hide();
this._trigger('close');
// ========== 练习2
this.isTooltipOpen = false;
// ==========
},
close: function () {
this._close();
}
});
})(jQuery);
// 练习2 测试代码
$('a').tooltip().hover(function () {
console.log('Is Tooltip Open? : ' + $(this).tooltip('isOpen').toString());
});
3.添加代码监听 tooltipOpen
事件,并在控制台中记录一条消息
$('a').bind('tooltipopen', function() {
console.log('tooltipopen event triggered');
});
4.**(挑战)**自定义提示条部件的 content
选项,通过 AJAX 取得链接的 href
属性指向的页面的内容,将取得的内容作为提示条的文本。
$('a').tooltip({
content: function () {
var url = $(this).attr('href');
var result = null;
$.ajax({
url: url,
type: 'get',
dataType: 'html',
async: false,
success: function (data) {
result = data;
}
});
return result;
}
});
5.**(挑战)**为提示条部件提供一个新的 effect 选项,如果指定该选项,则用该名字指定的 jQuery UI 效果显示或隐藏提示条。
// 在 _open 方法末尾加上:
// ========== 练习5
if (this.options.effect) {
this._tooltipDiv.effect(this.options.effect, {distance: 10, duration: 80});
}
// ==========
//练习5 测试代码
$('a').tooltip({effect: 'shake'});