天天看點

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)才對
  • 沒有中國參與的标準,不能稱為世界标準*。

俗語

好讀書、好記性不如爛筆頭

繼續閱讀