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

Rails 7中使用jQuery事件监听器时出现错误求助

Rails7 Turbo环境下JS错误排查与解决

问题场景

在Rails7中通过CDN在_head.html.erb的<head>标签引入jQuery:

<script src="https://code.jquery.com/jquery-3.7.1.min.js" integrity="sha256-/JqT3SQfawRcv/BIHPThkBvs0OEvtFFmqPF/lYI/Cxo=" crossorigin="anonymous"></script>

随后在show.html.erb中添加轮播相关jQuery代码:

<script>
var carouselWidth = $('.carousel-inner')[0].scrollWidth;
var cardWidth = $('.carousel-item').width();

var scrollPosition = 0;

$('.carousel-control-next').on('click', function(){
    console.log('next');
    scrollPosition = scrollPosition + cardWidth;
    $('.carousel-inner').animate({scrollLeft: scrollPosition}, 600);
});
</script>

浏览器控制台抛出错误:

menu-f19d6842a068966109ba6a8b3de0283869f632ec673afe222888bbeba8442401.js:7 Uncaught TypeError: Cannot read properties of null (reading 'addEventListener')
    at addToggleListener (menu-f19d6842a068966109ba6a8b3de0283869f632ec673afe222888bbeba8442401.js:7:26)
    at HTMLDocument.<anonymous> (menu-f19d6842a068966109ba6a8b3de0283869f632ec673afe222888bbeba8442401.js:15:9)
    at S (turbo.es2017-esm.js:344:34)
    at Object.notifyApplicationAfterPageLoad (turbo.es2017-esm.js:3158:16)
    at Object.pageBecameInteractive (turbo.es2017-esm.js:3080:14)
    at he.pageIsInteractive (turbo.es2017-esm.js:2486:27)
    at he.pageIsComplete (turbo.es2017-esm.js:2490:14)
    at HTMLDocument.interpretReadyState (turbo.es2017-esm.js:2458:22)
addToggleListener @ menu-f19d6842a068966109ba6a8b3de0283869f632ec673afe222888bbeba8442401.js:7
(anonymous) @ menu-f19d6842a068966109ba6a8b3de0283869f632ec673afe222888bbeba8442401.js:15
S @ turbo.es2017-esm.js:344
notifyApplicationAfterPageLoad @ turbo.es2017-esm.js:3158
pageBecameInteractive @ turbo.es2017-esm.js:3080
pageIsInteractive @ turbo.es2017-esm.js:2486
pageIsComplete @ turbo.es2017-esm.js:2490
interpretReadyState @ turbo.es2017-esm.js:2458

补充menu.js内容:

function addToggleListener(selected_id, menu_id, toggle_class) {
        let selected_element = document.querySelector(`#${selected_id}`);
        selected_element.addEventListener('click', function(event){
                event.preventDefault();
                let menu = document.querySelector(`#${menu_id}`)
                menu.classList.toggle(toggle_class);
        });
}

document.addEventListener('turbo:load', function(){
        addToggleListener('hamburger', 'navbar-menu', 'collapse');
        addToggleListener('account', 'dropdown-menu', 'active');
});

错误原因

核心问题是:document.querySelector(#${selected_id})返回null,即当前页面不存在对应ID的元素,但代码直接调用了addEventListener方法。

在Rails7默认启用Turbo的环境下,turbo:load事件会在每次页面加载(包括Turbo驱动的跳转)时触发。如果当前访问的页面(比如show.html.erb)没有#hamburger或#account元素,调用addToggleListener就会触发该错误。

同时,轮播的jQuery代码也存在潜在问题:直接写在页面中的JS会在DOM未完全渲染时执行,可能导致无法获取.carousel-inner等元素。

解决方案

1. 修复menu.js的空值判断

修改addToggleListener函数,先检查元素是否存在再绑定事件:

function addToggleListener(selected_id, menu_id, toggle_class) {
        let selected_element = document.querySelector(`#${selected_id}`);
        // 先判断触发元素是否存在
        if (!selected_element) return;
        
        selected_element.addEventListener('click', function(event){
                event.preventDefault();
                let menu = document.querySelector(`#${menu_id}`);
                // 再判断菜单元素是否存在
                if (!menu) return;
                menu.classList.toggle(toggle_class);
        });
}

document.addEventListener('turbo:load', function(){
        addToggleListener('hamburger', 'navbar-menu', 'collapse');
        addToggleListener('account', 'dropdown-menu', 'active');
});

2. 优化轮播jQuery代码的执行时机

由于jQuery放在<head>中,DOM未渲染完成时JS就会执行,需要将代码包裹在turbo:load事件或jQuery的ready方法中:

<script>
document.addEventListener('turbo:load', function() {
    var carouselInner = $('.carousel-inner')[0];
    if (!carouselInner) return;
    
    var carouselWidth = carouselInner.scrollWidth;
    var cardWidth = $('.carousel-item').width();

    var scrollPosition = 0;

    $('.carousel-control-next').on('click', function(){
        console.log('next');
        scrollPosition = scrollPosition + cardWidth;
        $('.carousel-inner').animate({scrollLeft: scrollPosition}, 600);
    });
});
</script>

或使用jQuery原生写法:

<script>
$(document).ready(function() {
    var carouselInner = $('.carousel-inner')[0];
    if (!carouselInner) return;
    
    var carouselWidth = carouselInner.scrollWidth;
    var cardWidth = $('.carousel-item').width();

    var scrollPosition = 0;

    $('.carousel-control-next').on('click', function(){
        console.log('next');
        scrollPosition = scrollPosition + cardWidth;
        $('.carousel-inner').animate({scrollLeft: scrollPosition}, 600);
    });
});
</script>

3. jQuery代码的其他存放方式

  • 多页面共用代码:可放在app/javascript/controllers/目录作为Turbo控制器,或写入app/javascript/application.js(需确保jQuery已加载)。
  • 页面特定代码:除直接写在show.html.erb外,还可通过content_for在页面中引入独立JS文件,或使用Turbo的页面特定加载逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 03:30:01