Search code examples
javascriptjquerynode.jsjquery-pluginsejs

Update data in 'breaking news ticker'


Info: I have a site for the local sportsclub that is showing the latest news. I have used an example code called 'Breaking News Ticker' as can be found here (example 4). Works great.

Problem: I want to update the news items every x minutes. These headlines are stored in a file and are send by the node-server if I visit ../data/headlines. The initial loading works but any subsequent runs of the code produce a Uncaught TypeError: $(...).breakingNews is not a function at fillTicker (schemeScripts.js:15:22).

What I've tried: Wrapping it all up in a function and then execute that function. Trigger the function with setInterval. This produces the error described above.

Code: scheme.ejs

<div class="bn-breaking-news bn-effect-scroll bn-direction-ltr" id="newsTicker" style="height: 40px; line-height: 38px; border-radius: 2px; border-width: 1px;">
  <div class="bn-label" style="background: #A32638;">NIEUWS</div>
  <div class="bn-news" style="right: 0px;">
    <ul>
      <li>
        <span class="bn-loader-text">Loading news items...</span>
      </li>
    </ul>
  </div>
</div>
<script type="text/javascript">
  $(document).ready(function () {
    $("#newsTicker").breakingNews({
      position : 'fixed-bottom',
      source: {
        type:'json',
        url:'../data/headlines', 
        limit:20, 
        showingField:'title',
        linkEnabled: false,
        target:'_blank', 
        seperator: '<span class="bn-seperator" style="background-image:url(./images/logo.png);"></span>',
        errorMsg: 'Json file not loaded. Please check the settings.'
      }
    });
    fillTicker()
    setInterval(fillTicker, msToNextHour())
  })
</script>

schemeScripts.js (containing fillTicker)

function fillTicker() {
    console.log("test2")
    $("#newsTicker").breakingNews({
        position : 'fixed-bottom',
        source: {
            type:'json',
            url:'../data/headlines', 
            limit:20, 
            showingField:'title',
            linkEnabled: false,
            target:'_blank', 
            seperator: '<span class="bn-seperator" style="background-image:url(./images/logo.png);"></span>',
            errorMsg: 'Json file not loaded. Please check the settings.'
        }
    });
}

I can sort of imagine that this would produce a TypeError but I really struggle/can't get my head around to produce code that does work. Preferably without loading the whole page again. Any help/hints appreciated!

Edit The file breaking-news-ticker.min.js is also loaded. Maybe the answer lies withing this file:

!function(p) {
    "use strict";
    p.breakingNews = function(e, t) {
        var i = {
            effect: "scroll",
            direction: "ltr",
            height: 40,
            fontSize: "default",
            themeColor: "default",
            background: "default",
            borderWidth: 1,
            radius: 2,
            source: "html",
            rss2jsonApiKey: "",
            play: !0,
            delayTimer: 4e3,
            scrollSpeed: 2,
            stopOnHover: !0,
            position: "auto",
            zIndex: 99999
        }
          , a = this;
        a.settings = {},
        a._element = p(e),
        a._label = a._element.children(".bn-label"),
        a._news = a._element.children(".bn-news"),
        a._ul = a._news.children("ul"),
        a._li = a._ul.children("li"),
        a._controls = a._element.children(".bn-controls"),
        a._prev = a._controls.find(".bn-prev").parent(),
        a._action = a._controls.find(".bn-action").parent(),
        a._next = a._controls.find(".bn-next").parent(),
        a._pause = !1,
        a._controlsIsActive = !0,
        a._totalNews = a._ul.children("li").length,
        a._activeNews = 0,
        a._interval = !1,
        a._frameId = null;
        var o = function() {
            if (0 < a._label.length && ("rtl" == a.settings.direction ? a._news.css({
                right: a._label.outerWidth()
            }) : a._news.css({
                left: a._label.outerWidth()
            })),
            0 < a._controls.length) {
                var e = a._controls.outerWidth();
                "rtl" == a.settings.direction ? a._news.css({
                    left: e
                }) : a._news.css({
                    right: e
                })
            }
            if ("scroll" === a.settings.effect) {
                var t = 0;
                a._li.each(function() {
                    t += p(this).outerWidth()
                }),
                t += 50,
                a._ul.css({
                    width: t
                })
            }
        }
          , s = function() {
            var l = new XMLHttpRequest;
            l.onreadystatechange = function() {
                if (4 == l.readyState && 200 == l.status) {
                    var e = JSON.parse(l.responseText)
                      , t = ""
                      , i = "";
                    switch (a.settings.source.showingField) {
                    case "title":
                        i = "title";
                        break;
                    case "description":
                        i = "description";
                        break;
                    case "link":
                        i = "link";
                        break;
                    default:
                        i = "title"
                    }
                    var s = "";
                    void 0 !== a.settings.source.seperator && void 0 !== typeof a.settings.source.seperator && (s = a.settings.source.seperator);
                    for (var n = 0; n < e.items.length; n++)
                        a.settings.source.linkEnabled ? t += '<li><a target="' + a.settings.source.target + '" href="' + e.items[n].link + '">' + s + e.items[n][i] + "</a></li>" : t += "<li><a>" + s + e.items[n][i] + "</a></li>";
                    a._ul.empty().append(t),
                    a._li = a._ul.children("li"),
                    a._totalNews = a._ul.children("li").length,
                    o(),
                    "scroll" != a.settings.effect && d(),
                    a._li.find(".bn-seperator").css({
                        height: a.settings.height - 2 * a.settings.borderWidth
                    }),
                    f()
                }
            }
            ,
            l.open("GET", "https://api.rss2json.com/v1/api.json?rss_url=" + a.settings.source.url + "&count=" + a.settings.source.limit + "&api_key=" + a.settings.source.rss2jsonApiKey, !0),
            l.send()
        }
          , n = function() {
            p.getJSON(a.settings.source.url, function(e) {
                var t = ""
                  , i = "";
                i = "undefined" === a.settings.source.showingField ? "title" : a.settings.source.showingField;
                var s = "";
                void 0 !== a.settings.source.seperator && void 0 !== typeof a.settings.source.seperator && (s = a.settings.source.seperator);
                for (var n = 0; n < e.length && !(n >= a.settings.source.limit); n++)
                    a.settings.source.linkEnabled ? t += '<li><a target="' + a.settings.source.target + '" href="' + e[n].link + '">' + s + e[n][i] + "</a></li>" : t += "<li><a>" + s + e[n][i] + "</a></li>",
                    "undefined" === e[n][i] && console.log('"' + i + '" does not exist in this json.');
                a._ul.empty().append(t),
                a._li = a._ul.children("li"),
                a._totalNews = a._ul.children("li").length,
                o(),
                "scroll" != a.settings.effect && d(),
                a._li.find(".bn-seperator").css({
                    height: a.settings.height - 2 * a.settings.borderWidth
                }),
                f()
            })
        }
          , l = function() {
            var e = parseFloat(a._ul.css("marginLeft"));
            e -= a.settings.scrollSpeed / 2,
            a._ul.css({
                marginLeft: e
            }),
            e <= -a._ul.find("li:first-child").outerWidth() && (a._ul.find("li:first-child").insertAfter(a._ul.find("li:last-child")),
            a._ul.css({
                marginLeft: 0
            })),
            !1 === a._pause && (a._frameId = requestAnimationFrame(l),
            window.requestAnimationFrame && a._frameId || setTimeout(l, 16))
        }
          , r = function() {
            var e = parseFloat(a._ul.css("marginRight"));
            e -= a.settings.scrollSpeed / 2,
            a._ul.css({
                marginRight: e
            }),
            e <= -a._ul.find("li:first-child").outerWidth() && (a._ul.find("li:first-child").insertAfter(a._ul.find("li:last-child")),
            a._ul.css({
                marginRight: 0
            })),
            !1 === a._pause && (a._frameId = requestAnimationFrame(r)),
            window.requestAnimationFrame && a._frameId || setTimeout(r, 16)
        }
          , c = function() {
            "rtl" === a.settings.direction ? a._ul.stop().animate({
                marginRight: -a._ul.find("li:first-child").outerWidth()
            }, 300, function() {
                a._ul.find("li:first-child").insertAfter(a._ul.find("li:last-child")),
                a._ul.css({
                    marginRight: 0
                }),
                a._controlsIsActive = !0
            }) : a._ul.stop().animate({
                marginLeft: -a._ul.find("li:first-child").outerWidth()
            }, 300, function() {
                a._ul.find("li:first-child").insertAfter(a._ul.find("li:last-child")),
                a._ul.css({
                    marginLeft: 0
                }),
                a._controlsIsActive = !0
            })
        }
          , u = function() {
            "rtl" === a.settings.direction ? (0 <= parseInt(a._ul.css("marginRight"), 10) && (a._ul.css({
                "margin-right": -a._ul.find("li:last-child").outerWidth()
            }),
            a._ul.find("li:last-child").insertBefore(a._ul.find("li:first-child"))),
            a._ul.stop().animate({
                marginRight: 0
            }, 300, function() {
                a._controlsIsActive = !0
            })) : (0 <= parseInt(a._ul.css("marginLeft"), 10) && (a._ul.css({
                "margin-left": -a._ul.find("li:last-child").outerWidth()
            }),
            a._ul.find("li:last-child").insertBefore(a._ul.find("li:first-child"))),
            a._ul.stop().animate({
                marginLeft: 0
            }, 300, function() {
                a._controlsIsActive = !0
            }))
        }
          , d = function() {
            switch (a._controlsIsActive = !0,
            a.settings.effect) {
            case "typography":
                a._ul.find("li").hide(),
                a._ul.find("li").eq(a._activeNews).width(30).show(),
                a._ul.find("li").eq(a._activeNews).animate({
                    width: "100%",
                    opacity: 1
                }, 1500);
                break;
            case "fade":
                a._ul.find("li").hide(),
                a._ul.find("li").eq(a._activeNews).fadeIn();
                break;
            case "slide-down":
                a._totalNews <= 1 ? a._ul.find("li").animate({
                    top: 30,
                    opacity: 0
                }, 300, function() {
                    p(this).css({
                        top: -30,
                        opacity: 0,
                        display: "block"
                    }),
                    p(this).animate({
                        top: 0,
                        opacity: 1
                    }, 300)
                }) : (a._ul.find("li:visible").animate({
                    top: 30,
                    opacity: 0
                }, 300, function() {
                    p(this).hide()
                }),
                a._ul.find("li").eq(a._activeNews).css({
                    top: -30,
                    opacity: 0
                }).show(),
                a._ul.find("li").eq(a._activeNews).animate({
                    top: 0,
                    opacity: 1
                }, 300));
                break;
            case "slide-up":
                a._totalNews <= 1 ? a._ul.find("li").animate({
                    top: -30,
                    opacity: 0
                }, 300, function() {
                    p(this).css({
                        top: 30,
                        opacity: 0,
                        display: "block"
                    }),
                    p(this).animate({
                        top: 0,
                        opacity: 1
                    }, 300)
                }) : (a._ul.find("li:visible").animate({
                    top: -30,
                    opacity: 0
                }, 300, function() {
                    p(this).hide()
                }),
                a._ul.find("li").eq(a._activeNews).css({
                    top: 30,
                    opacity: 0
                }).show(),
                a._ul.find("li").eq(a._activeNews).animate({
                    top: 0,
                    opacity: 1
                }, 300));
                break;
            case "slide-left":
                a._totalNews <= 1 ? a._ul.find("li").animate({
                    left: "50%",
                    opacity: 0
                }, 300, function() {
                    p(this).css({
                        left: -50,
                        opacity: 0,
                        display: "block"
                    }),
                    p(this).animate({
                        left: 0,
                        opacity: 1
                    }, 300)
                }) : (a._ul.find("li:visible").animate({
                    left: "50%",
                    opacity: 0
                }, 300, function() {
                    p(this).hide()
                }),
                a._ul.find("li").eq(a._activeNews).css({
                    left: -50,
                    opacity: 0
                }).show(),
                a._ul.find("li").eq(a._activeNews).animate({
                    left: 0,
                    opacity: 1
                }, 300));
                break;
            case "slide-right":
                a._totalNews <= 1 ? a._ul.find("li").animate({
                    left: "-50%",
                    opacity: 0
                }, 300, function() {
                    p(this).css({
                        left: "50%",
                        opacity: 0,
                        display: "block"
                    }),
                    p(this).animate({
                        left: 0,
                        opacity: 1
                    }, 300)
                }) : (a._ul.find("li:visible").animate({
                    left: "-50%",
                    opacity: 0
                }, 300, function() {
                    p(this).hide()
                }),
                a._ul.find("li").eq(a._activeNews).css({
                    left: "50%",
                    opacity: 0
                }).show(),
                a._ul.find("li").eq(a._activeNews).animate({
                    left: 0,
                    opacity: 1
                }, 300));
                break;
            default:
                a._ul.find("li").hide(),
                a._ul.find("li").eq(a._activeNews).show()
            }
        }
          , f = function() {
            if (a._pause = !1,
            a.settings.play)
                switch (a.settings.effect) {
                case "scroll":
                    "rtl" === a.settings.direction ? a._ul.width() > a._news.width() ? r() : a._ul.css({
                        marginRight: 0
                    }) : a._ul.width() > a._news.width() ? l() : a._ul.css({
                        marginLeft: 0
                    });
                    break;
                default:
                    a.pause(),
                    a._interval = setInterval(function() {
                        a.next()
                    }, a.settings.delayTimer)
                }
        }
          , _ = function() {
            a._element.width() < 480 ? (a._label.hide(),
            "rtl" == a.settings.direction ? a._news.css({
                right: 0
            }) : a._news.css({
                left: 0
            })) : (a._label.show(),
            "rtl" == a.settings.direction ? a._news.css({
                right: a._label.outerWidth()
            }) : a._news.css({
                left: a._label.outerWidth()
            }))
        };
        a.init = function() {
            if (a.settings = p.extend({}, i, t),
            "fixed-top" === a.settings.position ? a._element.addClass("bn-fixed-top").css({
                "z-index": a.settings.zIndex
            }) : "fixed-bottom" === a.settings.position && a._element.addClass("bn-fixed-bottom").css({
                "z-index": a.settings.zIndex
            }),
            "default" != a.settings.fontSize && a._element.css({
                "font-size": a.settings.fontSize
            }),
            "default" != a.settings.themeColor && (a._element.css({
                "border-color": a.settings.themeColor,
                color: a.settings.themeColor
            }),
            a._label.css({
                background: a.settings.themeColor
            })),
            "default" != a.settings.background && a._element.css({
                background: a.settings.background
            }),
            a._element.css({
                height: a.settings.height,
                "line-height": a.settings.height - 2 * a.settings.borderWidth + "px",
                "border-radius": a.settings.radius,
                "border-width": a.settings.borderWidth
            }),
            a._li.find(".bn-seperator").css({
                height: a.settings.height - 2 * a.settings.borderWidth
            }),
            a._element.addClass("bn-effect-" + a.settings.effect + " bn-direction-" + a.settings.direction),
            o(),
            "object" == typeof a.settings.source)
                switch (a.settings.source.type) {
                case "rss":
                    "rss2json" === a.settings.source.usingApi ? (s(),
                    0 < a.settings.source.refreshTime && setInterval(function() {
                        a._activeNews = 0,
                        a.pause(),
                        a._ul.empty().append('<li style="display:block; padding-left:10px;"><span class="bn-loader-text">......</span></li>'),
                        setTimeout(function() {
                            s()
                        }, 1e3)
                    }, 1e3 * a.settings.source.refreshTime * 60)) : ((l = new XMLHttpRequest).open("GET", "https://query.yahooapis.com/v1/public/yql?q=" + encodeURIComponent('select * from rss where url="' + a.settings.source.url + '" limit ' + a.settings.source.limit) + "&format=json", !0),
                    l.onreadystatechange = function() {
                        if (4 == l.readyState)
                            if (200 == l.status) {
                                var e = JSON.parse(l.responseText)
                                  , t = ""
                                  , i = "";
                                switch (a.settings.source.showingField) {
                                case "title":
                                    i = "title";
                                    break;
                                case "description":
                                    i = "description";
                                    break;
                                case "link":
                                    i = "link";
                                    break;
                                default:
                                    i = "title"
                                }
                                var s = "";
                                "undefined" != a.settings.source.seperator && void 0 !== a.settings.source.seperator && (s = a.settings.source.seperator);
                                for (var n = 0; n < e.query.results.item.length; n++)
                                    a.settings.source.linkEnabled ? t += '<li><a target="' + a.settings.source.target + '" href="' + e.query.results.item[n].link + '">' + s + e.query.results.item[n][i] + "</a></li>" : t += "<li><a>" + s + e.query.results.item[n][i] + "</a></li>";
                                a._ul.empty().append(t),
                                a._li = a._ul.children("li"),
                                a._totalNews = a._ul.children("li").length,
                                o(),
                                "scroll" != a.settings.effect && d(),
                                a._li.find(".bn-seperator").css({
                                    height: a.settings.height - 2 * a.settings.borderWidth
                                }),
                                f()
                            } else
                                a._ul.empty().append('<li><span class="bn-loader-text">' + a.settings.source.errorMsg + "</span></li>")
                    }
                    ,
                    l.send(null));
                    break;
                case "json":
                    n(),
                    0 < a.settings.source.refreshTime && setInterval(function() {
                        a._activeNews = 0,
                        a.pause(),
                        a._ul.empty().append('<li style="display:block; padding-left:10px;"><span class="bn-loader-text">......</span></li>'),
                        setTimeout(function() {
                            n()
                        }, 1e3)
                    }, 1e3 * a.settings.source.refreshTime * 60);
                    break;
                default:
                    console.log('Please check your "source" object parameter. Incorrect Value')
                }
            else
                "html" === a.settings.source ? ("scroll" != a.settings.effect && d(),
                f()) : console.log('Please check your "source" parameter. Incorrect Value');
            var l;
            a.settings.play ? a._action.find("span").removeClass("bn-play").addClass("bn-pause") : a._action.find("span").removeClass("bn-pause").addClass("bn-play"),
            a._element.on("mouseleave", function(e) {
                var t = p(document.elementFromPoint(e.clientX, e.clientY)).parents(".bn-breaking-news")[0];
                p(this)[0] !== t && (!0 === a.settings.stopOnHover ? !0 === a.settings.play && a.play() : !0 === a.settings.play && !0 === a._pause && a.play())
            }),
            a._element.on("mouseenter", function() {
                !0 === a.settings.stopOnHover && a.pause()
            }),
            a._next.on("click", function() {
                a._controlsIsActive && (a._controlsIsActive = !1,
                a.pause(),
                a.next())
            }),
            a._prev.on("click", function() {
                a._controlsIsActive && (a._controlsIsActive = !1,
                a.pause(),
                a.prev())
            }),
            a._action.on("click", function() {
                a._controlsIsActive && (a._action.find("span").hasClass("bn-pause") ? (a._action.find("span").removeClass("bn-pause").addClass("bn-play"),
                a.stop()) : (a.settings.play = !0,
                a._action.find("span").removeClass("bn-play").addClass("bn-pause")))
            }),
            _(),
            p(window).on("resize", function() {
                _(),
                a.pause(),
                a.play()
            })
        }
        ,
        a.pause = function() {
            a._pause = !0,
            clearInterval(a._interval),
            cancelAnimationFrame(a._frameId)
        }
        ,
        a.stop = function() {
            a._pause = !0,
            a.settings.play = !1
        }
        ,
        a.play = function() {
            f()
        }
        ,
        a.next = function() {
            !function() {
                switch (a.settings.effect) {
                case "scroll":
                    c();
                    break;
                default:
                    a._activeNews++,
                    a._activeNews >= a._totalNews && (a._activeNews = 0),
                    d()
                }
            }()
        }
        ,
        a.prev = function() {
            !function() {
                switch (a.settings.effect) {
                case "scroll":
                    u();
                    break;
                default:
                    a._activeNews--,
                    a._activeNews < 0 && (a._activeNews = a._totalNews - 1),
                    d()
                }
            }()
        }
        ,
        a.init()
    }
    ,
    p.fn.breakingNews = function(t) {
        return this.each(function() {
            if (null == p(this).data("breakingNews")) {
                var e = new p.breakingNews(this,t);
                p(this).data("breakingNews", e)
            }
        })
    }
}(jQuery);

Solution

  • The error indicates that the plugin is deleted, so make sure it's not being manipulated elsewhere.

    To achieve reload, the plugin needs to be reset, along with all data and listeners etc., so a proper way would be to extend the plugin with reset methods.

    A quick and dirty way to achieve that would be to replace ticker container in the interval function.

    // html
    let newsTickerHTML;
    
    $(document).ready(function () {
    
    // store ticker clean html to replace later
    newsTickerHTML = $('#newsTicker').parent().html();
    

    and later:

    function fillTicker() {
    
        // reset
        $('#newsTicker').parent().html(newsTickerHTML);
    

    code:

    let newsTickerHTML;
    
      $(document).ready(function () {
    
        // store ticker clean html to replace later
        newsTickerHTML = $('#newsTicker').parent().html();
    
    
        $("#newsTicker").breakingNews({
          position : 'fixed-bottom',
          source: {
            type:'json',
            url:'../data/headlines', 
            limit:20, 
            showingField:'title',
            linkEnabled: false,
            target:'_blank', 
            seperator: '<span class="bn-seperator" style="background-image:url(./images/logo.png);"></span>',
            errorMsg: 'Json file not loaded. Please check the settings.'
          }
        });
        fillTicker()
        setInterval(fillTicker, msToNextHour())
      });
    
    
    function fillTicker() {
    
        // reset
        $('#newsTicker').parent().html(newsTickerHTML);
    
        console.log("test2")
        $("#newsTicker").breakingNews({
            position : 'fixed-bottom',
            source: {
                type:'json',
                url:'../data/headlines', 
                limit:20, 
                showingField:'title',
                linkEnabled: false,
                target:'_blank', 
                seperator: '<span class="bn-seperator" style="background-image:url(./images/logo.png);"></span>',
                errorMsg: 'Json file not loaded. Please check the settings.'
            }
        });
    }
    

    edit

    Also wrap plugin HTML code into a parent container:

    <div>
        <div class="bn-breaking-news bn-effect-scroll bn-direction-ltr" id="newsTicker" style="height: 40px; line-height: 38px; border-radius: 2px; border-width: 1px;">
          <div class="bn-label" style="background: #A32638;">NIEUWS</div>
          <div class="bn-news" style="right: 0px;">
            <ul>
              <li>
                <span class="bn-loader-text">Loading news items...</span>
              </li>
            </ul>
          </div>
        </div>
    </div>
    

    edit 2

    Try merging code so that it runs in the template completely, and only depends on jQuery:

    <div>
        <div class="bn-breaking-news bn-effect-scroll bn-direction-ltr" id="newsTicker" style="height: 40px; line-height: 38px; border-radius: 2px; border-width: 1px;">
            <div class="bn-label" style="background: #A32638;">NIEUWS</div>
            <div class="bn-news" style="right: 0px;">
                <ul>
                    <li>
                        <span class="bn-loader-text">Loading news items...</span>
                    </li>
                </ul>
            </div>
        </div>
    </div>
    <script type="text/javascript">
    // html
    let newsTickerHTML;
    
    $(document).ready(function() {
    
        // store ticker clean html to replace later
        newsTickerHTML = $('#newsTicker').parent().html();
    
        function fillTicker() {
    
            console.log("test2")
    
            // reset
            $('#newsTicker').parent().html(newsTickerHTML);
    
    
            $("#newsTicker").breakingNews({
                position: 'fixed-bottom',
                source: {
                    type: 'json',
                    url: '../data/headlines',
                    limit: 20,
                    showingField: 'title',
                    linkEnabled: false,
                    target: '_blank',
                    seperator: '<span class="bn-seperator" style="background-image:url(./images/logo.png);"></span>',
                    errorMsg: 'Json file not loaded. Please check the settings.'
                }
            });
        }
    
        fillTicker()
        setInterval(fillTicker, msToNextHour())
    });
    </script>