iT邦幫忙

0

我之前有做野鳥圖鑑,改寫成react component+fetch讀json檔,有出現錯誤訊息,該怎麼處理呢?

  • 分享至 

  • xImage

各位大大好!我之前有做鳥類圖鑑是用display:flex+display:grid+fetch讀json檔的Responsive Filterable Image Gallery,我今天改寫成React function componenet+fetch讀json檔的Responsive Filterable Image Gallery,我編輯器是vscode+go livie,用React.js的cdn方式,但出現錯誤訊息和點左圖片後,右邊沒有出現圖片和文字說明,不知道是否用fetch讀json檔有問題,該怎麼辦呢?
/images/emoticon/emoticon04.gif/images/emoticon/emoticon02.gif

google瀏覽器上的錯誤訊息:
Inline Babel script:311 Uncaught (in promise) TypeError: Cannot set properties of null (setting 'innerHTML')at :733:27

程式碼部分:

 result1.innerHTML = `<img src=${bird.url} alt=${bird.name}/>`;
 result2.innerHTML ="<h1>" + bird.name + "</h1>" + "<p>" + bird.category + "," + bird.local + "," + bird.description + "</p>";

google瀏覽器出現錯誤照片部分:
https://ithelp.ithome.com.tw/upload/images/20230801/20145992FijoK2mWKh.png

執行後,點左邊圖片,右邊沒出現圖片和文字敘述,照片部分:
https://ithelp.ithome.com.tw/upload/images/20230801/201459929MBgEVPrfu.png

index.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    
    
    <script src="https://unpkg.com/react@18/umd/react.development.js" crossorigin></script>
    <script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js" crossorigin></script>
    <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    
    <div id="demo"></div>

    <script type="text/babel">

        let result1 = document.getElementById("divImg");
        let result2 = document.getElementById("divShow");

        
        
        function BirdGallery() {

            let list = document.querySelectorAll('.list');
            let itemBox = document.querySelectorAll('.itemBox');
            

            for (let i = 0; i < list.length; i++) {
                // ul 的 list 點擊監聽事件
                list[i].addEventListener('click', function() {
                for (let j = 0; j < list.length; j++) {
                    // 移除 .active 的 CSS
                    list[j].classList.remove('active');
                }

                // 每個 ul 的 list 新增 .active
                this.classList.add('active');

                // 取得 HTML 標籤上的 data-filter 屬性
                let dataFilter = this.getAttribute('data-filter');

                for (let k = 0; k < itemBox.length; k++) {
                    // 移除 .active 的 CSS
                    itemBox[k].classList.remove('active');
                    // 新增 .hide 的 CSS
                    itemBox[k].classList.add('hide');
                    // 判斷 div 的 data-item 是否等於 ul 的 dataFilter,或 dataFilter 等於 "all"
                    if (itemBox[k].getAttribute('data-item') == dataFilter || dataFilter == "all") {
                    // 移除 .hide 的 CSS
                    itemBox[k].classList.remove('hide');
                    // 新增 .active 的 CSS
                    itemBox[k].classList.add('active');
                    }
                }
                });
            }

            return (
                <div>

                    <header id="birdheader">
                        <p>鳥類圖鑑</p>
                    </header>

                    <main>
                        <section>
                        <ul>
                            <li className="list active" data-filter="all">全部</li>
                            <li className="list" data-filter="local">本土鳥類</li>
                            <li className="list" data-filter="transit">過境鳥、候鳥</li>
                            <li className="list" data-filter="all" onClick={() => Clear()}>清除</li>
                        </ul>
                        </section>

                    
                        <section className="birdsection">

                            <div className="bird">
                                <div className="itemBox" data-item="local" >
                                    <a href="#birdbox" onClick={()=>show('鳥名')}><img src="image/bird1.jpg"/></a>
                                </div>
                                <div className="itemBox" data-item="local" >
                                    <a href="#birdbox" onClick={()=>show('鳥名')} ><img src="image/bird2.jpg"/></a>
                                </div>
                                <div className="itemBox" data-item="local" >
                                    <a href="#birdbox" onClick={()=>show('鳥名')}><img src="image/bird3.jpg"/></a>
                                </div>
                                <div className="itemBox" data-item="local" >
                                    <a href="#birdbox" onClick={()=>show('鳥名')}><img src="image/bird4.jpg"/></a>
                                </div>
                                <div className="itemBox" data-item="local" >
                                    <a href="#birdbox" onClick={()=>show('鳥名')} ><img src="image/bird5.jpg"/></a>
                                </div>
                                

                                <div className="itemBox" data-item="local" >
                                    <a href="#birdbox" onClick={()=>show('鳥名')}><img src="image/bird6.jpg"/></a>
                                </div>
                                <div className="itemBox" data-item="local" >
                                    <a href="#birdbox" onClick={()=>show('鳥名')} ><img src="image/bird7.jpg"/></a>
                                </div>
                                <div className="itemBox" data-item="local" >
                                    <a href="#birdbox" onClick={()=>show('鳥名')}><img src="image/bird8.jpg"/></a>
                                </div>
                                <div className="itemBox" data-item="local" >
                                    <a href="#birdbox" onClick={()=>show('鳥名')}><img src="image/bird9.jpg"/></a>
                                </div>
                                <div className="itemBox" data-item="local" >
                                    <a href="#birdbox" onClick={()=>show('鳥名')} ><img src="image/bird10.jpg"/></a>
                                </div>
                                
                               
                            </div>
                                

                            </div>
                            <div id="birdbox">
                                <div id="divImg"></div>
                                <div className="block">
                                    <div id="divShow"></div>
                                    <button className="birdbtn"><a href="#birdheader">回到最上面</a></button>
                                </div>
                            </div>

                        </section>

                    </main>

                    <footer>
                        <div className="ft">
                            © 版權所有 2023 THY 鳥類網站
                        </div>
                    </footer>

                </div>
            );
         }
        
         function show(ns) {
            fetch("json/bird.json")
            .then(function(response) {
                return response.json(); // 選擇 JSON 格式
            })
            .then(function(data) {
                // 使用迴圈讀取 JSON 檔案內容並將資料存放在 bird 變數中
                for (let i = 0; i < data.length; i++) {
                    // 將 JSON 檔資料存放在 bird 中
                    let bird = data[i];
                    // 若鳥類名稱在 JSON 檔案中與傳遞過來的鳥類名稱相符,則顯示照片、鳥名和特徵;否則,不顯示內容
                    if (ns == bird.name) {
                        result1.innerHTML = `<img src=${bird.url} alt=${bird.name}/>`;
                        result2.innerHTML ="<h1>" + bird.name + "</h1>" + "<p>" + bird.category + "," + bird.local + "," + bird.description + "</p>";
                    }
                }
            });
        }

        // 外部定義 Clear 函式
        function Clear() {
            result1.innerHTML = "";
            result2.innerHTML = "";
        }



        const root = ReactDOM.createRoot(document.getElementById('demo')).render(<BirdGallery/>);

    </script>

   

</body>
</html>

style.css檔:

@import url('https://fonts.googleapis.com/css2?family=Lato:wght@100&family=Lobster&family=Montserrat&family=Noto+Sans+TC:wght@300&family=Poppins&family=Roboto&display=swap');

*{	
	margin: 0;
	padding: 0;
	box-sizing: border-box;
	font-family: 'Poppins', sans-serif;
}

header{
	height:100px;
	width: 100%;
	background-color:black;
	color: white;
	text-align: center;
}

header p{
	font-size:56px;
	position: relative;
	top:10%;
}

main{
	margin: 10px auto;
}

section ul{
	display: flex;
	justify-content: center;
	align-items: center;
	flex-wrap: wrap;
	margin-bottom: 20px;
}

section ul li{
	list-style: none;
	background: #eee;
	padding: 12px 20px;
	margin: 5px;
	letter-spacing: 1px;
	cursor: pointer;
}

section ul li.active{
	background:black;
	color: #fff;
}


.birdsection{
	display:grid;
	grid-template-columns: repeat(2,1fr);
}


.bird{
	display:grid;
	grid-template-columns: repeat(auto-fill, minmax(90px, 1fr));
	grid-template-rows: repeat(auto-fill, minmax(90px, 1fr)); 
	grid-row-gap:10px;
	grid-column-gap:5px;
	width: 100%;
	height:515px;
	overflow:scroll;
}

.bird::-webkit-scrollbar {
	width: 10px;
	height: 10px;
	background-color:#F8F8FF;
}

.bird::-webkit-scrollbar-thumb{
	background-color: #F8F8FF;
	border: 2px solid #F5F5F5;
}

.bird .itemBox{
	position: relative;
	width: 90px;
	height: 90px;
	margin: 5px;
	display: block;
}

.bird .itemBox.hide{
	display: none;
}

/*
object-fit:cover
object-fit 屬性指定元素的內容應該如何去適應指定容器的高度與寬度
cover:保持原有尺寸比例。但部分内容可能被剪切。
*/
.bird .itemBox img{
	position: absolute;
	top: 0;
	left: 0;
	width: 100%;
	height: 100%;
	object-fit: cover;
	cursor: pointer;
}

.bird .itemBox img:hover{
	opacity: 0;     /* 初始透明度為0,即完全透明 */
	animation: fadeInOut 3s infinite; /* 使用 fadeInOut 動畫,3s 表示動畫持續時間 */
}

@keyframes fadeInOut {
	0%, 100% {
	  opacity: 0; /* 開始和結束時完全透明 */
	}
	50% {
	  opacity: 1; /* 中間時完全不透明 */
	}
}

#birdbox{
	display:grid;
	grid-template-columns: repeat(2,1fr);
}


#divImg img{
	width:495px;
	height:495px;
	object-fit: cover;
}

#divImg img:hover{
	transform-origin: center; /* 設置縮放的基準點為圖片的中心 */
  	animation: fadeInOut_1 3s infinite; /* 使用 zoomInOut 動畫,3s 表示動畫持續時間 */
}

@keyframes fadeInOut_1{
	0%, 100% {
	  opacity: 0; /* 開始和結束時完全透明 */
	}
	50% {
	  opacity: 1; /* 中間時完全不透明 */
	}
}

.itemBox img,#divImg img{
	border-radius: 5%;
}

#divShow{
	width:450px;
	height:435px;
	overflow: scroll;
  	overflow-wrap: break-word;
  	white-space: normal;
	text-align: center;
	margin-left:35px;
	cursor: pointer;
}

#divShow:hover{
	background-color: #eee;
}



#divShow::-webkit-scrollbar {
	width: 10px;
	height: 10px;
	background-color:#F8F8FF;
}

#divShow::-webkit-scrollbar-thumb{
	background-color: #F8F8FF;
	border: 2px solid #F5F5F5;
}

#divShow h1{
	font-size:32px;
	text-align:center;
}

#divShow p{
	font-size:20px;
}

.birdbtn{
	width: 100px;
	height:50px;
	background-color:#F8F8FF;
	border: 1px solid #F5F5F5;
	border-radius: 10px;
	position: relative;
	left: 40%;
	right: 42%;
	margin-top: 10px;
	cursor: pointer;
}

.birdbtn a{
	
	text-decoration: none;
	color: black;
}

.birdbtn:hover{
	position: relative; /* 設置圖片為相對定位,以便進行平移 */
  	animation: zoomInOut 3s infinite; /* 使用 translateX 動畫,3s 表示動畫持續時間 */
}

@keyframes zoomInOut {
	0%, 100% {
	  transform: scale(1); /* 開始和結束時不進行縮放,即正常大小 */
	}
	50% {
	  transform: scale(1.5); /* 中間時進行縮放,即放大到原來的1.5倍 */
	}
}

footer{
	height:100px;
	width: 100%;
	background-color:black;
	color: white;
	text-align: center;
}

footer .ft{
	font-size:24px;
	position: relative;
	top: 35%;
}

@media screen and (max-width:415px){

	

	header{
		height:80px;
		width: 100%;
	}

	header p{
		font-size:48px;
		position: relative;
		top:5%;
	}

	.birdsection{
		display: grid;
		grid-template-columns: repeat(1,1fr);
	}
	
	.bird{
		display:grid;
		grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
		grid-template-rows: repeat(auto-fill, minmax(200px, 1fr)); 
		grid-row-gap:5px;
		grid-column-gap:5px;
		justify-items: center;
		align-items: center;
		width:100%;
		height: 625px;
	}

	.bird .itemBox{
		position: relative;
		width: 200px;
		height: 200px;
		margin: 5px;
		display: block;
	}

	

	#birdbox{
		display: grid;
		grid-template-columns: repeat(1,1fr);
		
	}
	
	#divImg img{
		width:200px;
		height:200px;
		object-fit: cover;
	}
	
	#divShow{
		width: 200px;
		height: 185px;
		
		
	}

	#divShow h1{
		font-size:24px;
	}

	#divShow p{
		font-size:16px;
	}
	
	.birdbtn{
		position: relative;
		left: 25%;
		right: 25%;
		margin:5px;
	}
	
	footer{
		height:80px;
		width: 100%;
		background-color:black;
		color: white;
		text-align: center;
	}
	
	footer .ft{
		font-size:18px;
		position: relative;
		top: 32%;
	}

}

@media screen and (max-width:290px){

	header{
		height:80px;
		width: 100%;
	}

	header p{
		font-size:48px;
		position: relative;
		top:5%;
	}

	#divShow h1{
		font-size:18px;
	}

	#divShow p{
		font-size:16px;
	}

	footer{
		height:80px;
		width: 100%;
		background-color:black;
		color: white;
		text-align: center;
	}
	
	footer .ft{
		font-size:18px;
		position: relative;
		top: 32%;
	}

}

json檔:

[
    {
        "name":"鳥名",
        "category":"科別",
        "local":"台灣本土鳥類",
        "description":"敘述",
        "url":"image/bird1.jpg"
    },
    {
        "name":"鳥名",
        "category":"科別",
        "local":"台灣本土鳥類",
        "description":"敘述",
        "url":"image/bird2.jpg"
    },
    {
        "name":"鳥名",
        "category":"科別",
        "local":"台灣本土鳥類",
        "description":"敘述",
        "url":"image/bird3.jpg"
    },
    {
        "name":"鳥名",
        "category":"科別",
        "local":"台灣本土鳥類",
        "description":"敘述",
        "url":"image/bird4.jpg"
    },
    {
        "name":"鳥名",
        "category":"科別",
        "local":"台灣本土鳥類",
        "description":"敘述",
        "url":"image/bird5.jpg"
    },
    {
        "name":"鳥名",
        "category":"科別",
        "local":"台灣本土鳥類",
        "description":"敘述",
        "url":"image/bird6.jpg"
    },
    {
        "name":"鳥名",
        "category":"科別",
        "local":"台灣本土鳥類",
        "description":"敘述",
        "url":"image/bird7.jpg"
    },
    {
        "name":"鳥名",
        "category":"科別",
        "local":"台灣本土鳥類",
        "description":"敘述",
        "url":"image/bird8.jpg"
    },
    {
        "name":"鳥名",
        "category":"科別",
        "local":"台灣本土鳥類",
        "description":"敘述",
        "url":"image/bird9.jpg"
    },
    {
        "name":"鳥名",
        "category":"科別",
        "local":"台灣本土鳥類",
        "description":"敘述",
        "url":"image/bird10.jpg"
    }	
]
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

2 個回答

0
iT邦新手 4 級 ‧ 2023-08-02 05:45:20

React 還沒產生出網頁元素就用這兩句,造成 result1 跟 result2 都變成 null,後續要指派 result1.innerHTML 屬性的內容當然就出錯了

    let result1 = document.getElementById("divImg");
    let result2 = document.getElementById("divShow");

也許你把const root = ReactDOM.createRoot(document.getElementById('demo')).render(<BirdGallery/>);放到 script 最前面就可以解決了?或是直到要指派 innerHTML 時才去取得 result1 跟 result2 也有機會解決這問題,不知道為何你要那麼早取得 result1 跟 result2?

謝謝妳!我試試看,你的做法!

0
harry xie
iT邦研究生 1 級 ‧ 2023-08-02 16:49:07
  1. 可以試著把這兩行搬到 BirdGallery 元件內,然後我的話會用 useRef 去取 DOM 元素,而不是用 JS 原生程式碼。
let result1 = document.getElementById("divImg");
let result2 = document.getElementById("divShow");

你看看你這兩行寫在 BirdGallery 元件內,網頁上就能正常的呈現多個鳥類圖片

let list = document.querySelectorAll('.list');
let itemBox = document.querySelectorAll('.itemBox');
  1. 可以搜尋 Prettier 去排版您的程式碼,讓程式碼看起來更整齊
  2. 這些程式碼具有高度相似性,您可以改寫成迴圈去跑(複製貼上有點跑版...
<div className="itemBox" data-item="local" >
    <a href="#birdbox" onClick={()=>show('鳥名')}><img src="image/bird1.jpg"/></a>
</div>
<div className="itemBox" data-item="local" >
    <a href="#birdbox" onClick={()=>show('鳥名')} ><img src="image/bird2.jpg"/></a>
</div>
<div className="itemBox" data-item="local" >
    <a href="#birdbox" onClick={()=>show('鳥名')}><img src="image/bird3.jpg"/></a>
</div>
<div className="itemBox" data-item="local" >
    <a href="#birdbox" onClick={()=>show('鳥名')}><img src="image/bird4.jpg"/></a>
</div>
<div className="itemBox" data-item="local" >
    <a href="#birdbox" onClick={()=>show('鳥名')} ><img src="image/bird5.jpg"/></a>
</div>


<div className="itemBox" data-item="local" >
    <a href="#birdbox" onClick={()=>show('鳥名')}><img src="image/bird6.jpg"/></a>
</div>
<div className="itemBox" data-item="local" >
    <a href="#birdbox" onClick={()=>show('鳥名')} ><img src="image/bird7.jpg"/></a>
</div>
<div className="itemBox" data-item="local" >
    <a href="#birdbox" onClick={()=>show('鳥名')}><img src="image/bird8.jpg"/></a>
</div>
<div className="itemBox" data-item="local" >
    <a href="#birdbox" onClick={()=>show('鳥名')}><img src="image/bird9.jpg"/></a>
</div>
<div className="itemBox" data-item="local" >
    <a href="#birdbox" onClick={()=>show('鳥名')} ><img src="image/bird10.jpg"/></a>
</div>

謝謝妳!

我要發表回答

立即登入回答