天天看点

H5+javascript+jQuery的单页面应用(SPA)(五)

前言

本文将优化 SPA 前端页面逻辑,

  1. 登录成功后,Login 菜单转变为 Logout;
  2. 点击 Logout 后,Member 页面也要退出,还原登入前的页面内容

优化前端页面

之前的文章为了方便用了静态方式编写,为了项目日后复杂度提升的时候,能有更好的兼容和实施效率,优化代码让项目更有实施弹性。

头部菜单以 Unordered List 实现

首先,修改 index.html <header><nav>。增加 nav id 及把原先的<div>内容更换成 <ul></ul>

<header>
    <nav id="headerNavigationMenu">
        <ul></ul>
    </nav>
</header>
           

把原来 index.css 关于 #headerNavigation div 相关的 css 全部删掉,换成如下:

#headerNavigationMenu ul{
    float: right;
    margin: 0; padding: 0;
    margin-right: 20px;
    cursor: pointer;
    font-weight:700;
    color:#555555ee;
}
#headerNavigationMenu ul li, #headerNavigationMenu ul span{
    list-style: none;
    margin: 0; padding: 0;
    float: left;
}
#headerNavigationMenu ul li.selected{
    font-weight: 700;
    color:#f36c01ee;
}
#headerNavigationMenu ul li.onHover{
    font-weight: 700;
    color:#000000;
}
           

最后在 index.js 里面作一下改动。因为修改的代码部分有点多,这里直接上整份 index.js 内容。可以自行比对一下:

$(function() {
    
    // 定义默认内容页面及菜单
    const defaultPage = "Login"
    // 定义当前选定的菜单ID
    let activeNavId = defaultPage
    
    var navIds = {}
    navIds.navHomeLink = "Home"
    navIds.navAboutUsLink = "AboutUs"
    navIds.navLoginLink = "Login"
    navIds.navMemberLink = "Member"
    
    // build a navigation menu link array
    // to filter out false click events from $(document).click(function(e) {})
    // if user not clicking on navigation menu, no change from displaying page
    let navItems = []
    for (let key in navIds) {
        navItems.push(key)
    }

    for(let key in navIds){
        
        $('#headerNavigationMenu ul').append("<li id='"+key+"'>"+navIds[key]+"</li>")
        
    }
 
    $('#headerNavigationMenu ul li').each(function() {
        _this = $(this)
        console.log("_this: "+_this.attr('id'))
        for (let key in navIds) {
            if (_this.attr('id') == key) {
                _this.attr('id', key)
                if(_this.attr('id') != $('#headerNavigationMenu ul li:last-child').attr('id')) {
                    _this.after("<span>&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;</span>")
                }
                if(_this.attr('id') == "navMemberLink") {
                    console.log("true")
                    _this.append(" <i class='bi bi-person-circle'></i>")
                }
            }
        }
        // console.log("$('#headerNavigationMenu ul li:last-child') - attr('id'): "+$('#headerNavigationMenu ul li:last-child').attr('id'))
    })
    
    // 初始化页面
    updateObjectDisplayStatus(navIds,defaultPage)

    $(document).click(function(e) {
        let clickedElementId = $(e.target).attr('id')
        // console.log('clickedELementId: '+clickedElementId)
        // console.log('navItems.includes(clickedElementId: '+navItems.includes(clickedElementId))
        
        headerNavigationHover()
        // menu only response when user clicked on the header navigation bar AND
        // if the menu items is not being selected already
        if (navItems.includes(clickedElementId) && (navIds[clickedElementId] != activeNavId)) {
            contentTransition(navIds[clickedElementId], activeNavId)
            updateObjectDisplayStatus(navIds,navIds[clickedElementId])
            activeNavId = navIds[clickedElementId]
        }
    })

    function updateObjectDisplayStatus(navIds, contentId) {
        for (let key in navIds) {
            //console.log(key+": "+navIds[key])
            if (navIds[key] == contentId) {
                console.log("clicked "+contentId)
                $('#'+contentId).css("display","block")
                $('#'+key).removeClass().addClass('selected')
            } else {
                $('#'+navIds[key]).css("display","none")
                $('#'+key).removeClass()
            }
        }
    }

    function contentTransition(fadeInComponent, fadeOutComponent) {
        $('#'+fadeInComponent).fadeIn()
        $('#'+fadeOutComponent).fadeOut()
    }

    function headerNavigationHover() {
        for (let key in navIds) {
            $('#'+key).hover(function() {
                if (activeNavId != navIds[key]) {
                    $("#"+key).addClass('onHover')
                }
            }, function() {
                if (activeNavId != navIds[key]) {
                    $("#"+key).removeClass()
                }
            })
        }
    }

    // ***********************************
    // Submit Login Form
    // ***********************************
    $('#ssoLoginForm').submit(function(e) {
        e.preventDefault()
        e.stopPropagation()

        $("#loginResultMessage").css("display","none")
        
        let ssoLoginForm = $('#ssoLoginForm')
        let ssoLoginFormObject = {}

        $('#ssoLoginForm :input').each(function() {
            if(this.id === "submitButton") {
                // continue
            } else {
                ssoLoginFormObject[this.id] = this.value
            }
        })

        console.log(JSON.stringify(ssoLoginFormObject))

        $.ajax({
            type: ssoLoginForm.attr('method'),
            url: ssoLoginForm.attr('action'),
            contentType: "application/json",
            data: JSON.stringify(ssoLoginFormObject),
            success: function(data) {
                if (data.code === 200) {
                    console.log("data to F/E: "+ JSON.stringify(data))
                    console.log("login status: "+data.objectToFE.status)
    
                    contentTransition("Member", activeNavId)
                    updateObjectDisplayStatus(navIds, "Member")
                    activeNavId = "Member"
                    $('#Member h1').html(data.objectToFE.status)
                } else if (data.code === 401 || data.code === 404) {
                    $('#loginResultMessage').fadeIn();
                    $('#loginResultMessage').html(data.description);
                    $('#loginResultMessage').css("display","block");
                }
                console.log("JSON.stringify(data): "+JSON.stringify(data))
            },
            error: function(e) {
                console.log("FAILED: JSON.stringify(e): "+JSON.stringify(e));
            }
        })
    })
})
           

新的 index.js 重复代码的问题都解决了,实施弹性比之前高很多。

后端部分代码不用改。启动后端应用和确保 Nginx 反向代理运作正常后,重新测试一下 SPA 看看修改后的样子和功能有没有问题。

H5+javascript+jQuery的单页面应用(SPA)(五)

测试全部通过,完成优化。

后续更新内容

SPA 加入登录状态 (session id)及相关更新。

区分登录前后状态

登录状态由两个部分组成:

  1. 后端发送过来的 session id
  2. 后端发送 session id 到前端 的 timestamp

源代码

关注我并发表不少于10字评论可以获取本文源代码。

码农经典语录

Linus Torvalds

Talk is cheap, show me the code.

蜂窝码农
  • DRY Principle (Don’t Repeat Yourself) 是做技术的最大笑话, DRY Principle应该改成 DORY Principle (Do Repeat Yourself)才对
  • 没有中国参与的标准,不能称为世界标准*。

俗语

好读书、好记性不如烂笔头

继续阅读