13-Javascript-Magic
在本章,我们将添加一个功能,当你将鼠标悬停在用户的昵称上时,会弹出一个漂亮的窗口。
社交网站的常见用户交互模式是,当你将鼠标悬停在用户的名称上时,可以在弹出窗口中显示用户的主要信息。 如果你从未注意到这一点,请访问Twitter,Facebook,LinkedIn或任何其他主要社交网站,当你看到用户名时,只需将鼠标指针放在上面几秒钟即可看到弹出窗口。
本章的GitHub链接为: Source, Diff, Zip

服务器端支持

在深入研究客户端之前,让我们先了解一下支持这些用户弹窗所需的服务器端的工作。 用户弹窗的内容将由新路由返回,它是现有个人主页路由的简化版本。
viewmodel 我们偷下懒,由于Popup的 vm 和 Profile 的相似,我们直接在 vm/profile.go中加入 GetPopupVM 来获得 Popup 的 vm
vm/profile.go
1
...
2
// GetPopupVM func
3
func (ProfileViewModelOp) GetPopupVM(sUser, pUser string) (ProfileViewModel, error) {
4
v := ProfileViewModel{}
5
v.SetTitle("Profile")
6
u, err := model.GetUserByUsername(pUser)
7
if err != nil {
8
return v, err
9
}
10
v.ProfileUser = *u
11
v.Editable = (sUser == pUser)
12
if !v.Editable {
13
v.IsFollow = u.IsFollowedByUser(sUser)
14
}
15
v.FollowersCount = u.FollowersCount()
16
v.FollowingCount = u.FollowingCount()
17
v.SetCurrentUser(sUser)
18
return v, nil
19
}
20
21
...
Copied!
controller/home.go
1
...
2
r.HandleFunc("/user/{username}/popup", popupHandler)
3
4
...
5
func popupHandler(w http.ResponseWriter, r *http.Request) {
6
tpName := "popup.html"
7
vars := mux.Vars(r)
8
pUser := vars["username"]
9
sUser, _ := getSessionUser(r)
10
vop := vm.ProfileViewModelOp{}
11
v, err := vop.GetPopupVM(sUser, pUser)
12
if err != nil {
13
msg := fmt.Sprintf("user ( %s ) does not exist", pUser)
14
w.Write([]byte(msg))
15
return
16
}
17
templates[tpName].Execute(w, &v)
18
}
19
20
...
Copied!
templates/content/popup.html
1
<table>
2
<tr valign="top">
3
<td width="64" style="border: 0px;"><img src="{{.ProfileUser.Avatar}}&s=64"></td>
4
<td style="border: 0px;">
5
<small>
6
<p><a href="/user/{{.ProfileUser.Username}}">{{.ProfileUser.Username}}</a></p>
7
{{if .ProfileUser.AboutMe}}
8
<p>{{ .ProfileUser.AboutMe }}</p>
9
{{end}}
10
11
{{if .ProfileUser.LastSeen}}
12
<p>Last seen on: {{ .ProfileUser.FormattedLastSeen }}</p>
13
{{end}}
14
15
<p>{{ .FollowersCount }} followers, {{ .FollowingCount }} following.</p>
16
17
{{if .Editable}}
18
<p><a href="/profile_edit">Edit your profile</a></p>
19
{{else}}
20
{{if .IsFollow}}
21
<p><a class="btn btn-outline-primary" href="/unfollow/{{.ProfileUser.Username}}">Unfollow</a></p>
22
{{else}}
23
<p><a class="btn btn-outline-primary" href="/follow/{{.ProfileUser.Username}}">Follow</a></p>
24
{{end}}
25
{{end}}
26
</small>
27
</td>
28
</tr>
29
</table>
Copied!
当用户将鼠标指针悬停在用户名上时,随后小节中编写的JavaScript代码将会调用此路由。客户端将服务器端返回的响应中的html内容显示在弹出窗口中。 当用户移开鼠标时,弹出窗口将被删除。 听起来很简单,对吧?
如果你想了解弹窗像什么样,现在可以运行应用,跳转到任何用户的个人主页,然后在地址栏的URL中追加/popup以查看全屏版本的弹出窗口内容。
本小节 Diff

客户端Ajax

我们在 _base.html 中加入 popup 的 Ajax,这样所有继承它的页面也同样继承了 popup的功能
templates/_base.html
1
...
2
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js" integrity="sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49" crossorigin="anonymous"></script>
3
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js" integrity="sha384-ChfqqxuZUCnJSK3+MXmPNIyE6ZbWh2IMqE241rYiqJxyMiZ6OW/JmZQ5stwEULTy" crossorigin="anonymous"></script>
4
<script>
5
$(function () {
6
var timer = null;
7
var xhr = null;
8
$('.user_popup').hover(
9
function(event) {
10
// mouse in event handler
11
var elem = $(event.currentTarget);
12
timer = setTimeout(function() {
13
timer = null;
14
xhr = $.ajax(
15
'/user/' + elem.first().text().trim() + '/popup').done(
16
function(data) {
17
xhr = null;
18
elem.popover({
19
trigger: 'manual',
20
html: true,
21
animation: false,
22
container: elem,
23
content: data
24
}).popover('show');
25
}
26
);
27
}, 1000);
28
},
29
function(event) {
30
// mouse out event handler
31
var elem = $(event.currentTarget);
32
if (timer) {
33
clearTimeout(timer);
34
timer = null;
35
}
36
else if (xhr) {
37
xhr.abort();
38
xhr = null;
39
}
40
else {
41
elem.popover('hide');
42
}
43
}
44
);
45
});
46
</script>
47
48
...
Copied!
然后我们在需要有 Popup 功能的地方,就是所有的用户Post的头像地方加入 class='user_popup'
templates/content/index.html & explore.html & profile.html
1
...
2
<td><span class="user_popup"><a href="/user/{{.User.Username}}">{{ .User.Username }}</a></span> said {{.FormattedTimeAgo}}:<br>{{ .Body }}</td>
3
...
Copied!
13-01
本小节 Diff

Links