第一次部署Chrome Extension- undureck typeError:无法读取NULL的属性(读取'长度')

发布于 01-21 19:16 字数 8737 浏览 3 评论 0原文

我正在尝试部署我的第一个JavaScript应用程序,这是Chrome扩展名。 这只是生成随机密码,并使用当前活动选项卡的URL存储。

应用程序在本地运行良好,但是将其部署到Chrome之后,我得到了此错误:

”在此处输入图像描述”

unturew typeerror:无法读取空的属性(读取“长度”) index.js:65(匿名函数)

我是初学者,因此对我的代码的任何批评都得到了高度赞赏。 太感谢了。

function render()  {

 *line65*   **if(passwords.length === 0)** {
   document.getElementById("saved-passwords-container").style.display= "none";
    }   else {
        document.getElementById("saved-passwords-container").style.display= "unset";
    }

    let list = ""
    **for (let i = 0; i < passwords.length; i++)** {
        list += `<div class="saved-password-line"><span>${passwords[i]}</span></br></br><span class="link"><a target='_blank'href='${links[i]}'>${links[i]}</a></span></div>`
    }

    document.getElementById("passwords-el").innerHTML = list
}

这是完整的index.js文件:

    var characters = [];
        for (var i=32; i<127; i++)
        characters.push(String.fromCharCode(i));

        for( var i = 0; i < characters.length; i++){                          
            if ( characters[i] === '<') { 
                characters.splice(i, 1); 
                i--; 
            }
        }
        for( var i = 0; i < characters.length; i++){                          
            if ( characters[i] === '>') { 
                characters.splice(i, 1); 
                i--; 
            }
        }

    
let pw1El = document.getElementById("pw1-el")
let pw1 = ""

let passwords = []
passwords = JSON.parse(localStorage.getItem("savedPasswords"))


let links = []
links = JSON.parse(localStorage.getItem("savedLinks"))

render()

document.getElementById("char-count-el").value = 20


document.getElementById("gen-btn").addEventListener("click", function() {
    
    var charCount = document.getElementById("char-count-el").value
    pw1 = ""

    
    for(let i = 0; i < charCount; i++) {
        let randomIndex = Math.floor(Math.random() * characters.length)
        pw1 += (characters[randomIndex])
    }

    pw1El.textContent = pw1

})

document.getElementById("save-btn").addEventListener("click", function() {
    passwords.push(pw1El.innerText)
    chrome.tabs.query({active: true, currentWindow: true}, function(tabs){
        links.push(tabs[0].url)
    })

    localStorage.setItem("savedPasswords", JSON.stringify(passwords))
    localStorage.setItem("savedLinks", JSON.stringify(links))

    render()
})

function render()  {

   **if(passwords.length === 0)** {
   document.getElementById("saved-passwords-container").style.display= "none";
    }   else {
        document.getElementById("saved-passwords-container").style.display= "unset";
    }

    let list = ""
    **for (let i = 0; i < passwords.length; i++)** {
        list += `<div class="saved-password-line"><span>${passwords[i]}</span></br></br><span class="link"><a target='_blank'href='${links[i]}'>${links[i]}</a></span></div>`
    }
    
    document.getElementById("passwords-el").innerHTML = list
}

document.getElementById("clear-btn").addEventListener("click", function() {
    passwords = []
    links = []

    localStorage.setItem("savedPasswords", JSON.stringify(passwords))
    localStorage.setItem("savedLinks", JSON.stringify(links))

    render()
})

document.getElementById("copy-btn").addEventListener("click", function() {
    var input = document.getElementById("pw1-el").textContent;   
    navigator.clipboard.writeText(input);

    alert("Copied Text: " + input);
})

index.html

<!DOCTYPE html>
<html>
<head>
        <link rel="stylesheet" href="index.css">
</head>

<body>
        <div class="container">
            <h1>Generate a</br>random password</h1>
            <p>Never use an unsecure password again.</p>
        <hr>
            <div>
                <label for="char-count-el">Character Count:</label>
                <input type="number" id="char-count-el">
                <button id="gen-btn"><span>Generate password</span></button>
            </div>
            <div>
                <label>Your Password:</label>
                <div class="pw-container">
                    <span class="password-line" id="pw1-el">...</span>
                    <button class="side-btn" id="save-btn">SAVE</button> 
                    <button class="side-btn" id="copy-btn">COPY</button> 
                </div>
            </div>
            <div id="saved-passwords-container">
                <hr>
                    <label>Saved Passwords:</label>
                    <div class="pw-container">
                        <div id="passwords-el">...</div>
                        <button class="side-btn" id="clear-btn">CLEAR</button>
                    </div>
            </div>
        </div>

        <script src="index.js"></script>
</body>

</html>

index.css

body {
    padding: 0;
    margin: 0;
    font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
    background-color: #ffffff;
    color: white;
    display: flex;
    justify-content: center;
    align-items: center;
}

h1::first-line {
    color: white;
}

h1 {
    color: #00ffaa;
    margin-bottom: 5px;
    line-height: 1;
}

label {
    font-size: 11px;
    display: block;
    color: #D5D4D8;
    margin-top: 10px;
}


input {
    height: 38px;
    border-radius: 5px;
    border: none;
    width: 70px;
    padding: 0px 10px;
    text-align: center;
    background-color: #D5D4D8;
    margin-right: 20px;
    font-size: 14px;
}

.container {
    background: #1F2937;
    margin: 0;
    padding: 10px 30px 40px;
    width: 100%;
    min-width: 500px;
    box-shadow: 0px 10px 30px 10px #2640644b;
    display: flex;
    flex-direction: column;
}

.pw-container {
    display: flex;
    border-radius: 5px;
    background-color: #3e4f66;
    padding: 10px;
    margin-top: 10px;
}

.password-line {
    color: #00ffaa;
    font-size: 16px;
    padding: 5px 10px;
    margin-top: 0px;
    flex-grow: 1;
    flex: 1 1 1;
    min-width: 0;
    word-wrap: break-word;
    white-space: pre-wrap;
    word-break: break-word;
}

#passwords-el {
    padding-right: 30px;
    flex-grow: 1;
    flex: 1 1 0;
    min-width: 0;
    word-wrap: break-word;
    white-space: pre-wrap;
    word-break: break-word;
}
.saved-password-line {
    color: #D5D4D8;
    font-size: 14px;
    padding: 10px 15px;
    border-bottom: solid 1px #d5d4d814;
    border-radius: 5px;
    margin-bottom: 10px;
    line-height: 0.9;
}

a {
    color: #d5d4d872;
    text-decoration: underline;
}

.side-btn {
    font-size: 12px;
    width: 60px;
    border: none;
    background: none;
    color: #D5D4D8;
    padding: 5px 10px;
    border-radius: 5px;
    justify-self: flex-end;
}

.side-btn:hover {
    background-color: #ffffff28 ;
}

#gen-btn {
    color: #ffffff;
    background: #0EBA80;
    text-transform: capitalize;
    text-align: center;
    width: 200px;
    height: 40px;
    padding: 10px 10px;
    border: none;
    border-radius: 5px;
    margin-bottom: 10px;
    margin-top: 10px;
    transition: all 0.5s;
    box-shadow: 0px 0px 30px 5px #0eba8135
}

#gen-btn:hover {
    box-shadow: 0px 0px 30px 10px #0eba8157
}

    #gen-btn span {
        cursor: pointer;
        display: inline-block;
        position: relative;
        transition: 0.5s;
    }

    #gen-btn span:after {
        content: '\279c';
        position: absolute;
        opacity: 0;
        top: 0;
        right: -20px;
        transition: 0.5s;
    }

    #gen-btn:hover span {
        padding-right: 25px;
    }

    #gen-btn:hover span:after {
        opacity: 1;
        right: 0;
    }

p {
    color: #D5D4D8;
    margin-top: 0px;
}

hr {
    border-width: 1px 0px 0px 0px;
    border-color: #95959576;
    margin: 15px 0;
}

subtest.json

{
    "manifest_version": 3,
    "version": "1.0",
    "name": "Password Generator",
    "action": {
        "default_popup": "index.html",
        "default_icon": "icon.png"
    },
    "permissions": [
        "tabs"
    ]
}

I am trying to deploy my first javascript application, which is a Chrome extension.
This simply generates random passwords and stores it with the url of current active tab.

App runs fine on local but after deploying it to Chrome, I got this error:

enter image description here

Uncaught TypeError: Cannot read properties of null (reading 'length')
index.js:65 (anonymous function)

I am a beginner, so any kind of criticism about my code is highly appreciated.
Thank you so much.

function render()  {

 *line65*   **if(passwords.length === 0)** {
   document.getElementById("saved-passwords-container").style.display= "none";
    }   else {
        document.getElementById("saved-passwords-container").style.display= "unset";
    }

    let list = ""
    **for (let i = 0; i < passwords.length; i++)** {
        list += `<div class="saved-password-line"><span>${passwords[i]}</span></br></br><span class="link"><a target='_blank'href='${links[i]}'>${links[i]}</a></span></div>`
    }

    document.getElementById("passwords-el").innerHTML = list
}

Here is the full index.js file:

    var characters = [];
        for (var i=32; i<127; i++)
        characters.push(String.fromCharCode(i));

        for( var i = 0; i < characters.length; i++){                          
            if ( characters[i] === '<') { 
                characters.splice(i, 1); 
                i--; 
            }
        }
        for( var i = 0; i < characters.length; i++){                          
            if ( characters[i] === '>') { 
                characters.splice(i, 1); 
                i--; 
            }
        }

    
let pw1El = document.getElementById("pw1-el")
let pw1 = ""

let passwords = []
passwords = JSON.parse(localStorage.getItem("savedPasswords"))


let links = []
links = JSON.parse(localStorage.getItem("savedLinks"))

render()

document.getElementById("char-count-el").value = 20


document.getElementById("gen-btn").addEventListener("click", function() {
    
    var charCount = document.getElementById("char-count-el").value
    pw1 = ""

    
    for(let i = 0; i < charCount; i++) {
        let randomIndex = Math.floor(Math.random() * characters.length)
        pw1 += (characters[randomIndex])
    }

    pw1El.textContent = pw1

})

document.getElementById("save-btn").addEventListener("click", function() {
    passwords.push(pw1El.innerText)
    chrome.tabs.query({active: true, currentWindow: true}, function(tabs){
        links.push(tabs[0].url)
    })

    localStorage.setItem("savedPasswords", JSON.stringify(passwords))
    localStorage.setItem("savedLinks", JSON.stringify(links))

    render()
})

function render()  {

   **if(passwords.length === 0)** {
   document.getElementById("saved-passwords-container").style.display= "none";
    }   else {
        document.getElementById("saved-passwords-container").style.display= "unset";
    }

    let list = ""
    **for (let i = 0; i < passwords.length; i++)** {
        list += `<div class="saved-password-line"><span>${passwords[i]}</span></br></br><span class="link"><a target='_blank'href='${links[i]}'>${links[i]}</a></span></div>`
    }
    
    document.getElementById("passwords-el").innerHTML = list
}

document.getElementById("clear-btn").addEventListener("click", function() {
    passwords = []
    links = []

    localStorage.setItem("savedPasswords", JSON.stringify(passwords))
    localStorage.setItem("savedLinks", JSON.stringify(links))

    render()
})

document.getElementById("copy-btn").addEventListener("click", function() {
    var input = document.getElementById("pw1-el").textContent;   
    navigator.clipboard.writeText(input);

    alert("Copied Text: " + input);
})

index.html

<!DOCTYPE html>
<html>
<head>
        <link rel="stylesheet" href="index.css">
</head>

<body>
        <div class="container">
            <h1>Generate a</br>random password</h1>
            <p>Never use an unsecure password again.</p>
        <hr>
            <div>
                <label for="char-count-el">Character Count:</label>
                <input type="number" id="char-count-el">
                <button id="gen-btn"><span>Generate password</span></button>
            </div>
            <div>
                <label>Your Password:</label>
                <div class="pw-container">
                    <span class="password-line" id="pw1-el">...</span>
                    <button class="side-btn" id="save-btn">SAVE</button> 
                    <button class="side-btn" id="copy-btn">COPY</button> 
                </div>
            </div>
            <div id="saved-passwords-container">
                <hr>
                    <label>Saved Passwords:</label>
                    <div class="pw-container">
                        <div id="passwords-el">...</div>
                        <button class="side-btn" id="clear-btn">CLEAR</button>
                    </div>
            </div>
        </div>

        <script src="index.js"></script>
</body>

</html>

index.css

body {
    padding: 0;
    margin: 0;
    font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
    background-color: #ffffff;
    color: white;
    display: flex;
    justify-content: center;
    align-items: center;
}

h1::first-line {
    color: white;
}

h1 {
    color: #00ffaa;
    margin-bottom: 5px;
    line-height: 1;
}

label {
    font-size: 11px;
    display: block;
    color: #D5D4D8;
    margin-top: 10px;
}


input {
    height: 38px;
    border-radius: 5px;
    border: none;
    width: 70px;
    padding: 0px 10px;
    text-align: center;
    background-color: #D5D4D8;
    margin-right: 20px;
    font-size: 14px;
}

.container {
    background: #1F2937;
    margin: 0;
    padding: 10px 30px 40px;
    width: 100%;
    min-width: 500px;
    box-shadow: 0px 10px 30px 10px #2640644b;
    display: flex;
    flex-direction: column;
}

.pw-container {
    display: flex;
    border-radius: 5px;
    background-color: #3e4f66;
    padding: 10px;
    margin-top: 10px;
}

.password-line {
    color: #00ffaa;
    font-size: 16px;
    padding: 5px 10px;
    margin-top: 0px;
    flex-grow: 1;
    flex: 1 1 1;
    min-width: 0;
    word-wrap: break-word;
    white-space: pre-wrap;
    word-break: break-word;
}

#passwords-el {
    padding-right: 30px;
    flex-grow: 1;
    flex: 1 1 0;
    min-width: 0;
    word-wrap: break-word;
    white-space: pre-wrap;
    word-break: break-word;
}
.saved-password-line {
    color: #D5D4D8;
    font-size: 14px;
    padding: 10px 15px;
    border-bottom: solid 1px #d5d4d814;
    border-radius: 5px;
    margin-bottom: 10px;
    line-height: 0.9;
}

a {
    color: #d5d4d872;
    text-decoration: underline;
}

.side-btn {
    font-size: 12px;
    width: 60px;
    border: none;
    background: none;
    color: #D5D4D8;
    padding: 5px 10px;
    border-radius: 5px;
    justify-self: flex-end;
}

.side-btn:hover {
    background-color: #ffffff28 ;
}

#gen-btn {
    color: #ffffff;
    background: #0EBA80;
    text-transform: capitalize;
    text-align: center;
    width: 200px;
    height: 40px;
    padding: 10px 10px;
    border: none;
    border-radius: 5px;
    margin-bottom: 10px;
    margin-top: 10px;
    transition: all 0.5s;
    box-shadow: 0px 0px 30px 5px #0eba8135
}

#gen-btn:hover {
    box-shadow: 0px 0px 30px 10px #0eba8157
}

    #gen-btn span {
        cursor: pointer;
        display: inline-block;
        position: relative;
        transition: 0.5s;
    }

    #gen-btn span:after {
        content: '\279c';
        position: absolute;
        opacity: 0;
        top: 0;
        right: -20px;
        transition: 0.5s;
    }

    #gen-btn:hover span {
        padding-right: 25px;
    }

    #gen-btn:hover span:after {
        opacity: 1;
        right: 0;
    }

p {
    color: #D5D4D8;
    margin-top: 0px;
}

hr {
    border-width: 1px 0px 0px 0px;
    border-color: #95959576;
    margin: 15px 0;
}

manifest.json

{
    "manifest_version": 3,
    "version": "1.0",
    "name": "Password Generator",
    "action": {
        "default_popup": "index.html",
        "default_icon": "icon.png"
    },
    "permissions": [
        "tabs"
    ]
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

瑾夏年华2025-01-28 19:16:27

我解决了。
我知道这一点(如果我错了,请纠正我)
如果本地存储为空,则解析时不会返回空数组。

显然,当我这样做时:

passwords = JSON.parse(localStorage.getItem("savedPasswords"))

密码不再是一个数组。
相反,我使用:

passwords.push(JSON.parse(localStorage.getItem("savedPasswords")))

但这只是将嵌套阵列在密码内推动。
因此,我添加了一个for循环,并使用IF语句来解决初始错误:

let locSavedPasswords = localStorage.getItem("savedPasswords")

if(locSavedPasswords !== null) {
    for( var i = 0; i < (JSON.parse(locSavedPasswords)).length; i++){                          
        passwords.push(JSON.parse(locSavedPasswords)[i])  
    }}

I solved it.
I understand that (please correct me if I'm wrong)
if the local storage is empty, it does not return an empty array when parsed.

Apparently, when I do:

passwords = JSON.parse(localStorage.getItem("savedPasswords"))

passwords is no longer an array.
I instead use:

passwords.push(JSON.parse(localStorage.getItem("savedPasswords")))

But that just pushes a nested array inside passwords.
So I added a for loop, and used an if statement to address the initial error:

let locSavedPasswords = localStorage.getItem("savedPasswords")

if(locSavedPasswords !== null) {
    for( var i = 0; i < (JSON.parse(locSavedPasswords)).length; i++){                          
        passwords.push(JSON.parse(locSavedPasswords)[i])  
    }}
你丑哭了我2025-01-28 19:16:27

最初,保存的Passewwordslocalstorage中不存在,因此localstorage.getItem('SavedPasswords')将返回null null 。

然后,您进行JSON.PARSE(null),它不会立即崩溃,因为null首先被胁迫到字符串并变为'null'然后将JSON优先处理,然后回到null,因为带有内容的字符串null是有效的JSON。

但是,您然后在其上进行.length并崩溃。

解决方案是处理尚未设置该项目的情况并像JSON分解的空数组一样处理它。您可以这样做,例如使用无效的合并操作员??

let passwords = JSON.parse(localStorage.getItem("savedPasswords") ?? '[]')

或者,您可以像以前一样以[]来保持初始化,但可以用实际值包装分配在某个条件下:

let passwords = []
const json = localStorage.getItem('savedPasswords')
if (json !== null) {
  passwords = JSON.parse(json)
}

我个人喜欢在localstorage中为结构化数据做的事情,这也是这样的,它还处理了其他事物(例如Invalid JSON)以某种方式存储在那里的情况(没有砖砌应用程序):

let passwords = []
try {
  const data = JSON.parse(localStorage.getItem('savedPasswords'))
  if (Array.isArray(data)) passwords = data
} catch {}

Initially, savedPasswords won't exist in localStorage, so localStorage.getItem('savedPasswords') will return null.

You then do JSON.parse(null), which doesn't immediately crash because null is first coerced to a string and becomes 'null' which is then JSON-parsed and turns back to null since the string with contents null is valid JSON.

But you then do .length on it and crash.

The solution is to handle the case where the item is not yet set and handle it like it was a JSON-stringified empty array. You can do so for example using the nullish coalescing operator ??:

let passwords = JSON.parse(localStorage.getItem("savedPasswords") ?? '[]')

Or, you can keep initializing it with [] as you did before but wrap the assignment with the actual value in a condition:

let passwords = []
const json = localStorage.getItem('savedPasswords')
if (json !== null) {
  passwords = JSON.parse(json)
}

Personally, what I like to do for structured data in localStorage is something like this, which also handles the case that other things like invalid JSON somehow got stored there (without bricking the application):

let passwords = []
try {
  const data = JSON.parse(localStorage.getItem('savedPasswords'))
  if (Array.isArray(data)) passwords = data
} catch {}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文