You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

导入JS函数后HTML自定义选择菜单消失且功能失效的原因

问题描述

我借助他人帮助实现了一个自定义样式的HTML选择菜单,预期用户点击「Next」按钮时,网站向数据库发送SQL请求,返回匹配所选音乐流派的首位艺术家。

我的代码如下:

HTML文件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Custom Select Menu</title>
    <link rel="stylesheet" href="main.css">
</head>
<body>
    <header>
        <h1>Welcome</h1>
    </header>

    <div class="genre" id="musicGenre">
        <h2>Choose your favorite Music Genre::</h2>

        <form action="">
            <select name="musicGenre" class="musicGenre">
                <option value="hiphop">HipHop</option>
                <option value="classic">Classic Music</option>
                <option value="pop">Pop</option>
                <option value="techno">Techno</option>
            </select>
        </form>

        <button onclick="nextStep()">Next</button>
    </div>

    <div id="result" style="display: none;">
        <h2>Result:</h2>
        <p id="artistResult"></p>
    </div>

    <script src="main.js"></script>
</body>
</html>

main.js(未添加import时正常)

var genre;

class MusicGenre {
    constructor(originalSelect) {
        this.originalSelect = originalSelect;
        this.customSelect = document.createElement("div");
        this.customSelect.classList.add("genreSelect");

        this.originalSelect.querySelectorAll("option").forEach(optionElement => {
            const itemElement = document.createElement("div");

            itemElement.classList.add("select__item");
            itemElement.textContent = optionElement.textContent;
            this.customSelect.appendChild(itemElement);

            if (optionElement.selected) {
                this._select(itemElement);
            }

            itemElement.addEventListener("click", () => {
                if (
                    this.originalSelect.multiple
                    && itemElement.classList.contains("select__item--selected")
                ) {
                    this._deselect(itemElement);
                } else {
                    this._select(itemElement);
                    genre = Array.from(this.customSelect.children).indexOf(itemElement);
                }
            });
        });

        this.originalSelect.insertAdjacentElement("afterend", this.customSelect);
        this.originalSelect.style.display = "none";
    }

    _select(itemElement) {
        const index = Array.from(this.customSelect.children).indexOf(itemElement);

        if (!this.originalSelect.multiple) {
            this.customSelect.querySelectorAll(".select__item").forEach(element => {
                element.classList.remove("select__item--selected")
            });
        }

        this.originalSelect.querySelectorAll("option")[index].selected = true;
        itemElement.classList.add("select__item--selected");
    }

    _deselect(itemElement) {
        const index = Array.from(this.customSelect.children).indexOf(itemElement);

        this.originalSelect.querySelectorAll("option")[index].selected = false;
        itemElement.classList.remove("select__item--selected");
    }
}

document.querySelectorAll(".musicGenre").forEach(selectElement => {
    new MusicGenre(selectElement);
})

function nextStep() {
    document.getElementById("result").style.display = "block";
    document.getElementById("artTypeCategory").style.display = "none";
    
    const result = await getArtistName(genre+1);
    const prompText = JSON.stringify(result[0]);

    document.getElementById("artTypeResult").innerHTML = promptText;
}

main.css

.genreSelect{
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    max-width: 300px;
    text-align: center;
    gap: 1px;
}

.select__item{
    padding: 10px;
    cursor: pointer;
    font-family: "Heebo", sans-serif;
    border-radius: 3px;
    background: #eeeeee;
    transition: background 0.1s;
}

.select__item--selected{
    background: #009578;
    color: #ffffff;
}

database.js

import mysql from 'mysql2'

const pool = mysql.createPool({
    host: 'localhost',
    user: 'root',
    password: '',
    database: 'artist'
}).promise()

export async function getArtistName(id) {
    const [rows] = await pool.query(`
    SELECT Name
    FROM Artists
    WHERE ID = ?
    `, id)
    return rows
}

原本菜单显示正常,但在main.js顶部添加import { getArtistName } from "./database.js"语句后,菜单变为原生样式,「Next」按钮也失效,无法获取SQL结果。请问该导入操作为何会破坏代码?


问题原因
  1. ES模块未被正确识别
    添加import语句后,main.js变成了ES模块,但HTML中引用脚本时没有添加type="module"属性,浏览器会把它当作普通脚本解析,import语法直接报错,导致后续所有JS代码(包括自定义选择菜单的逻辑)停止执行,菜单因此回到原生样式。

  2. 模块作用域隔离全局变量
    ES模块有独立作用域,原来的genre变量和nextStep函数会被限制在模块内部,而HTML按钮的onclick="nextStep()"是在全局作用域调用,自然找不到该函数,导致按钮失效。

  3. Node.js模块无法在浏览器环境运行
    database.js依赖的mysql2是Node.js专属模块,浏览器环境无法识别它,就算解决了模块加载问题,也会因缺少Node.js环境而报错,前端不能直接连接数据库。


修复方案
  1. 正确加载ES模块
    修改HTML中的脚本引用,添加type="module"属性:

    <script type="module" src="main.js"></script>
    
  2. 解决全局作用域调用问题
    把nextStep函数挂载到全局对象,或者改用事件监听:

    • 方案一(挂载全局):在main.js末尾添加
      window.nextStep = nextStep;
      
    • 方案二(事件监听):修改HTML按钮为<button id="nextBtn">Next</button>,然后在main.js中添加
      document.getElementById('nextBtn').addEventListener('click', nextStep);
      
  3. 改用后端接口实现数据库查询
    前端不能直接连接数据库,需通过后端接口中转:

    • 用Node.js+Express写后端接口(示例):
      // server.js
      import express from 'express';
      import { getArtistName } from './database.js';
      
      const app = express();
      app.use(express.json());
      
      app.get('/api/artist/:id', async (req, res) => {
        const id = req.params.id;
        const artist = await getArtistName(id);
        res.json(artist);
      });
      
      app.listen(3000, () => {
        console.log('Server running on port 3000');
      });
      
    • 修改前端nextStep函数:
      async function nextStep() {
        document.getElementById("result").style.display = "block";
        document.getElementById("musicGenre").style.display = "none"; // 修正原代码中不存在的artTypeCategory
      
        try {
          const response = await fetch(`http://localhost:3000/api/artist/${genre + 1}`);
          const result = await response.json();
          document.getElementById("artistResult").textContent = result[0]?.Name || '未找到艺术家';
        } catch (error) {
          console.error('获取艺术家失败:', error);
          document.getElementById("artistResult").textContent = '加载艺术家失败';
        }
      }
      

内容的提问来源于stack exchange,提问作者Tobi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 17:17:32