I have a javascript that stops working when a button is not showed. How can I fix it? - javascript

I've got 4 buttons and each button opens a section and closes the other. There is a possibility that one or the buttons are stay hidden because of a switch in the backend which you can toggle to make it visible (or to hide).
Now, if one or more buttons are not visible the script stops working. If I toggle all the buttons on, the buttons display and the script is working.
I have this code and it works fine (as long as all the buttons are visible):
<style>
.elementor-editor-active .hidden {
display:block;
}
.hidden{
display:none;
}
.shown{
display: block !important;
}
.btn_active_state{
background-color: #FFCC00 !important;
}
.btn_active_state a{
font-weight: bold !important;
}
</style>
<script>
var divs
var btn1 = document.getElementById("btn1");
var btn2 = document.getElementById("btn2");
var btn3 = document.getElementById("btn3");
var btn4 = document.getElementById("btn4");
btn1.onclick = function(event){
event.preventDefault();
toggleDivs("sect1",this);
};
btn2.onclick = function(event){
event.preventDefault();
toggleDivs("sect2",this);
};
btn3.onclick = function(event){
event.preventDefault();
toggleDivs("sect3",this);
};
btn4.onclick = function(event){
event.preventDefault();
toggleDivs("sect4",this);
};
function toggleDivs(s,btn){
if(btn.classList.contains("btn_active_state")){
document.getElementById(s).classList.remove("shown");
btn.classList.remove("btn_active_state");
document.getElementById(s).classList.remove("shown");
return;
}else{
btn1.classList.remove("btn_active_state");
btn2.classList.remove("btn_active_state");
btn3.classList.remove("btn_active_state");
btn4.classList.remove("btn_active_state");
btn.classList.add("btn_active_state");
document.getElementById("sect1").classList.remove("shown");
document.getElementById("sect2").classList.remove("shown");
document.getElementById("sect3").classList.remove("shown");
document.getElementById("sect4").classList.remove("shown");
document.getElementById(s).classList.add("shown","fade");
}
}
//force button1 state initialise, if required
//btn1.focus();
//btn1.click();
</script>
The script stops working when I toggle one or more buttons to OFF (so they don't display on the front-end). I saw solutions with a Try Catch method, but I don't know (if this is the right way to fix it), where to implement it...
I am using Elementor in Wordpress to accomplish this. (HTML widget where I pasted this code and named the sections and buttons to the names named in the code).
Side note: I'm not an expert in Javascript.
Thanks advance!

While I had 4 Elementor widgets (I used this as buttons) with every widget a unique ID (#btn1, #btn2, #btn3 & #btn4). I had also 4 different Elementor sections with their own ID (#sect1, #sect2, #sect3 & #sect4). If you clicked one of the button, the respective section would display (btn1 shows sect1, btn2 shows sect2 etc..).
The standard script (posted in first place) worked fined when all the buttons (btn1, btn2 etc.) where visible (not rendered, not in the code) on the page. This is because I used the Dynamic Visibility options in the 'Advanced' tab in Elementor (Crocoblock JetEngine/Elementor Pro).
The problem arose when 1 or more buttons were missing because of the Dynamic Visibility (If a switch in the CPT turned off, it will not show the widget). The script stopped working and none of the buttons worked again.
Now, I used this code in a HTML widget and placed the HTML widget under the sections (above the sections are the widgets acting like buttons). The first part of the script gives an 'active' state to the button you click on. Assure that you set the class of the button you want to have an active state as default, to 'active':
<style>
.elementor-editor-active .hidden {
display:block;
}
.hidden{
display:none;
}
.shown{
display: block !important;
}
#btn1, #btn2, #btn3, #btn4 {
cursor: pointer;
}
.active h2 {
font-weight: bold !important;
}
</style>
<script>
var activeButton = document.getElementById("btn1");
function toggleActive(btn) {
if (activeButton && activeButton != btn) {
activeButton.classList.remove("active");
}
activeButton = btn;
btn.classList.add("active");
}
var sect1 = document.getElementById("sect1");
var sect2 = document.getElementById("sect2");
var sect3 = document.getElementById("sect3");
var sect4 = document.getElementById("sect4");
try {
var btn1 = document.getElementById("btn1");
btn1.onclick = function(event){
toggleActive(btn1);
document.getElementById("sect1").classList.add("shown");
document.getElementById("sect2").classList.remove("shown");
document.getElementById("sect3").classList.remove("shown");
document.getElementById("sect4").classList.remove("shown");
};
} catch (error) {
console.log("btn1 not found on the page. Error: " + error);
}
try {
var btn2 = document.getElementById("btn2");
btn2.onclick = function(event){
toggleActive(btn2);
document.getElementById("sect1").classList.remove("shown");
document.getElementById("sect2").classList.add("shown");
document.getElementById("sect3").classList.remove("shown");
document.getElementById("sect4").classList.remove("shown");
};
} catch (error) {
console.log("btn1 not found on the page. Error: " + error);
}
try {
var btn3 = document.getElementById("btn3");
btn3.onclick = function(event){
toggleActive(btn3);
document.getElementById("sect1").classList.remove("shown");
document.getElementById("sect2").classList.remove("shown");
document.getElementById("sect3").classList.add("shown");
document.getElementById("sect4").classList.remove("shown");
};
} catch (error) {
console.log("btn1 not found on the page. Error: " + error);
}
try {
var btn4 = document.getElementById("btn4");
btn4.onclick = function(event){
toggleActive(btn4);
document.getElementById("sect1").classList.remove("shown");
document.getElementById("sect2").classList.remove("shown");
document.getElementById("sect3").classList.remove("shown");
document.getElementById("sect4").classList.add("shown");
};
} catch (error) {
console.log("btn1 not found on the page. Error: " + error);
}
</script>

OK. I have finished this up. you can extend it easily by adding to the buttons list ( at the top of the document ). This is a system to create the buttons, action and sections. It allows you to toggle between them, essentially like tabs. It is likely not the best implementation, but it is a working system, something to get started with. It separates the views from the buttons, two functions to control the state of each. one is controlling the buttons, the other the views. this should be your approach moving forward... Keep It Stupid Simple. Avoid complexity, embrace readability. if you want code to be ultra compact and "efficient", use webpack :D
//define buttons
const buttons = [
{ id:'btn1',
section:'sect1',
label:'Button 1'},
{ id:'btn2',
section:'sect2',
label:'Button 2'},
{ id:'btn3',
section:'sect3',
label:'Button 3'},
{ id:'btn4',
section:'sect4',
label:'Button 4'}
];
//console.table(buttons);
const views = [];
// create buttons and assign click, create sections
for ( const button of buttons){
let nB = document.createElement('button');
nB.id=button.id;
nB.innerText=button.label;
nB.onclick=(e)=>{
e.preventDefault();
toggleView( button.section, views );
toggleButton( button.id, buttons );
}
document.getElementById('menu').appendChild(nB);
// create the span / divs
let nD = document.createElement('div');
nD.id=button.section;
// put something in here for testing
nD.innerText=button.section;
nD.classList.add('hidden');
views.push(button.section);
document.getElementById('viewer').appendChild(nD);
}
// function to change sections
function toggleView( view, viewList ){
//console.log( view );
//console.log( viewList );
// hide all
for ( const v of viewList ){
// not actually the elements, but just reference
document.getElementById( v ).classList.remove('shown');
// unhide 'view' by id
if ( v == view ) {
//console.log(`view ${v} found`);
document.getElementById( v ).classList.add('shown');
}
}
}
function toggleButton( button, buttonList ){
// console.log(button);
// console.log(buttonList);
// enable all
// disable this button id
// iterate through managed list
for ( const b of buttonList ){
// console.log(b);
document.getElementById(b.id).classList.add('btn_active_state');
if ( b.id == button ) document.getElementById(b.id).classList.remove('btn_active_state');
}
}
toggleButton( buttons[0], buttons );
.elementor-editor-active .hidden {
display:block;
}
.hidden{
display:none;
}
.shown{
display: block !important;
}
.btn_active_state{
background-color: #FFCC00 !important;
}
.btn_active_state a{
font-weight: bold !important;
}
<html>
<body>
<div id='menu'></div>
<div id='viewer'></div>
</body>
</html>

Does this help? I've replaced 'this' with the btn var itself when calling toggleDivs(). So rather than toggleDivs("sect1", this) use toggleDivs("sect1", btn1)
btn1.onclick = function(event){
event.preventDefault();
toggleDivs("sect1",btn1);
};
btn2.onclick = function(event){
event.preventDefault();
toggleDivs("sect2",btn2);
};
btn3.onclick = function(event){
event.preventDefault();
toggleDivs("sect3",btn3);
};
btn4.onclick = function(event){
event.preventDefault();
toggleDivs("sect4",btn4);
};
// try to assign the var btn1 to the element
var btn1 = document.getElementById('btn1');
// check to see if it is valid / not null
if ( btn1 ){
// if it gets here, then the button is valid
btn1.onclick = (e) =>{
// show / hide sections
}
}
//otherwise, if btn1==null it will continue to the next

Related

Load a script using nodes included by another script

I need to make a web page with a lot of content. In order to be more efficient when modifying this content, I decided to put it in separate files and then include these files, following this tutorial: https://www.w3schools.com/howto/howto_html_include.asp.
For example one of these files may contain some clickable links to book descriptions, which are modal boxes. So I need to get them in a loading script to get these clickable links and make them trigger some events. But it seems this loading script is called before JavaScript gets the included nodes, even if I add an event listener after reading some threads (I tried to run it at 'DOMContentLoaded' or 'load') : document.getElementById or document.getElementsByClassName still returns null so it fails to define an onclick function. Let me show an example code:
script.js
function includeHTML() { /* Some code replacing the div by some-content.html, which is : <a id="clickable">Hello</a> */}
var button = null
window.addEventListener('load', function() {
button = document.getElementById("clickable");
button.onclick = function() { alert('Hello'); }
});
index.html
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" src="script.js"></script>
</head>
<body>
<p>Here is a trigger button : </p>
<div include-html="some-content.html"></div>
<script>includeHTML();</script>
</body>
</html>
On Firefox, this will fail on defining button.onclick as button is still null.
Any idea on how to fix it?
Not only should I be adding links, but also modal boxes. Here is a script code, more complete, for what my guess was:
script.js
var boxNames = ["bibliography", "about", "book1", "book2" ];
var boxes = null /* Contains the boxes to be displayed */
var trigs = null /* Contains the trigger buttons for each box */
var close = null /* Contains the close buttons for each box */
function setTrigger(i) {
trigs[i].onclick = function() { setBoxVisible(true, i); }
}
function setClose(i) {
trigs[i].onclick = function() { setBoxVisible(false, i); }
}
function load() {
boxes = new Array(4);
trigs = new Array(4);
close = new Array(4);
for(var i = 0; i < boxNames.length; i++) {
boxes[i]=document.getElementById(boxNames[i]+"-box");
trigs[i]=document.getElementById(boxNames[i]+"-trig");
close[i]=document.getElementById(boxNames[i]+"-close");
setTrigger(i); setClose(i);
}
}
window.onload = function() { load(); }
For the code of includeHTML(), you can have a look at the tutorial I shared, I copy/pasted.
I think this kind of function would be more elegant if dealing with such stuff, but I would need it to be launched once everything is loaded, as if I was running it manually.
Your code only added the event listener when the page was loading, likely before the link existed.
You need to delegate from the nearest static container.
Here in your code it is document
Give the link a class instead of ID and do
window.addEventListener('load', function(e) {
document.addEventListener('click', function(e) {
const tgt = e.target;
if (tgt.classList.contains("clickable")) {
e.preventDefault(); // because it is a link
alert('Hello');
}
});
});
<a class="clickable" href="#">Click</a>
Update after new code
You overwrite the trigs code
You can very simply extend my code so you do not need to loop
window.addEventListener('load', function(e) {
document.addEventListener('click', function(e) {
const tgt = e.target;
if (tgt.classList.contains("clickable")) {
e.preventDefault(); // because it is a link
alert('Hello');
}
else if (tgt.classList.contains("box")) {
e.preventDefault(); // because it is a link
const [id, what] = tgt.id.split("-")
console.log(id)
if (what === "trig") {
document.getElementById(id).classList.remove("hide")
}
else if (what === "close") {
document.getElementById(id).classList.add("hide"); // or tgt.closest("div").classList.add("hide")
}
}
});
});
.hide { display:none; }
<a class="clickable" href="#">Click</a>
<hr/>
<a class="box" id="bibliography-trig" href="#">Trigger Biblio</a>
<a class="box" id="about-trig" href="#">Trigger About</a>
<div id="bibliography" class="hide">Biblio
<a class="box" id="bibliography-close" href="#">Close</a>
</div>
<div id="about" class="hide">About
<a class="box" id="about-close" href="#">Close</a>
</div>

Prevent Background Scroll on Modal Box

I am using a plugin which has a modal pop up box for login (CM Registration Pro). I would like the background (body of my website) to not be able to scroll when the modal pop up is open.
I have tried the following code but am unable to get it to work
jQuery(document).ready(function ($) {
if($('.cmreg-overlay').length){
$('html').css('overflow', 'hidden');
}
});
If you would like to see the problem live my website is Redec
A Quick example that should work is:-
var element = document.querySelector('body');
element.addEventListener('click',function(){
if(element.classList.contains('.cmreg-overlay-visible')){
document.documentElement.style.overflow = 'hidden';
}
else{
document.documentElement.style.overflow = 'visible';
}
});
As it stands, your body has no background. If you mean you don't want your body to scroll when the modal box is open, then you can check whether .cmreg-overlay is shown, and if so then set overflow:hidden to the body.
I will assume that only #menu-item-16137, #menu-item-16194 and #menu-item-16195 are Here is how to do it:
// Prevent the body from scrolling when the modal opens
var x = document.querySelectorAll("#menu-item-16137, #menu-item-16194, #menu-item-16195");
for(var i = 0; i < x.length; i++) {
x[i].addEventListener("click", function() {
if($(".cmreg-overlay").style.display != none) {
document.body.style.overflow = hidden;
}
});
}
// Return the body back to scrolling when closing the modal
document.querySelector(".cmreg-overlay").addEventListener("click", function() {
document.body.style.overflow = "auto";
}

How do I add a button to a div class on load from Javascript? [duplicate]

This question already has answers here:
How to add onload event to a div element
(26 answers)
Closed 5 years ago.
For an assignment, I cannot touch the HTML code and am editing an external JS file. I have to refer the code to an existing class and turn that into a button to run a script.
The has to be ran on load to transform an element with a given id into a button that can also run a function on click.
So let's say the we have id="bar",
how do I go about it?
My code doesn't work at all.
document.getElementById("bar").onload = function () { myFunction() };
function myFunction() {
document.getElementById("bar").innerHTML = "<button></button>";
}
Why don't you just execute your script as the DOM is ready? To do so,
document.addEventListener('DOMContentLoaded', function () {
document.getElementById("bar").innerHTML = "<button></button>";
}, false);
You just need a createElement function.
This works:
document.addEventListener("DOMContentLoaded", function(){
var button = document.createElement("button");
button.innerHTML = "This is a button";
// assuming the Div's ID is bar
var div = document.getElementById('bar');
div.appendChild(button);
//the following function will alert a window when the button is clicked
button.addEventListener ("click", function() {
alert("Button was clicked");
});
});
Updated Codepen
I think this is bit tha you needed
var bar = document.getElementById('bar');
window.onload = function() {
var barInner = bar.innerHTML;
bar.innerHTML = '<button>' + barInner + '</button>';
}
bar.onclick = function() {
alert("Hello\nHow are you?");
};
document.getElementById("bar").onload = myFunction();
function myFunction() {
document.getElementById("bar").innerHTML = "<button>Button</button>";
}
There you go!
Not every single HTML element has a load event.
Only some of them are concerned, such as the window, an image... etc
Have a look here on MDN to learn more about this.
Here is a simple snippet resolving all what you mentioned.
window.addEventListener("load", () => {
// you can put your entire script in here.
var elt = document.getElementById("bar"),
button = document.createElement("button");
button.textContent = elt.textContent;
button.onclick = callback;
elt.textContent = '';
elt.appendChild(button);
function callback() {
console.log("The button has been clicked");
}
});
<div id="bar" style="background: beige; height: 2em">Click me</div>
In the previous snippet, I am appending the button in the element. But if the matter is really to transform it into a button, there we go:
window.addEventListener("load", () => {
// you can put your entire script in here.
var elt = document.getElementById("bar"),
container = elt.parentNode,
button = document.createElement("button");
button.id = elt.id; // if you want to keep the id
button.textContent = elt.textContent;
button.onclick = callback;
container.removeChild(elt);
container.appendChild(button);
function callback() {
console.log("The button has been clicked");
}
});
<div style="background: #fee; height: 2em">
<div id="bar" style="background: beige; height: 2em">Click me</div>
</div>

jQuery hover function to display an overlay but closes when hover displayed content

I am a little new to JS / jQuery and have done a hover function so when hover a link it shows a hidden DIV area but when I go to then hover over the div area shown of course it closes. I would like that if I then go into the content area won't close but stay open but then if leave that area would close?
JS:
$('.mini-cart').hover(
function () {
cartOpen();
},
function () {
cartClose();
}
);
var overlay = $("#cart_slide");
var cartContainer = $("#cart_over");
function cartOpen() {
cartContainer.fadeIn("slow");
overlay.addClass("overlay");
}
function cartClose() {
cartContainer.fadeOut("medium");
overlay.removeClass("overlay");
}
HTML:
<a class="mini-cart">hover link</a>
<div id="cart_over" style="display:none;">testing</div>
You could introduce the usage of a flag (isCartOpen) variable which is going to control the whether the DIV should be displayed or not.
See this working JSFiddle example and find below the related code:
var isCartOpen = false;
$('.mini-cart, #cart_over').hover(
function() {
isCartOpen = true;
cartOpen();
},
function() {
isCartOpen = false;
setTimeout(cartClose, 1000); // after 1 sec
}
);
var overlay = $("#cart_slide");
var cartContainer = $("#cart_over");
function cartOpen() {
cartContainer.fadeIn("slow");
overlay.addClass("overlay");
}
function cartClose() {
if (isCartOpen)
return;
cartContainer.fadeOut("medium");
overlay.removeClass("overlay");
}
Looks like you'll need to separate your open/close functions . Look at the following:
$('.mini-cart').hover(
cartOpen();
);
// You'll need to create a close button.. ex. <div class="close-button"></div>
$(".close-button").click(function(){
cartClose();
});

need to modify this jquery pop menu script to work with ajax

I am using this script from: http://pop.seaofclouds.com/
The problem is if you call the script multiple times it causes a cascading effect of a pop-out within a pop-out for as many times as you call the script.
I'm trying to figure out how to prevent it from executing when the popout has already been set. Here's the script:
//
// pop! for jQuery
// v0.2 requires jQuery v1.2 or later
//
// Licensed under the MIT:
// http://www.opensource.org/licenses/mit-license.php
//
// Copyright 2007,2008 SEAOFCLOUDS [http://seaofclouds.com]
//
(function($) {
$.pop = function(options){
// inject html wrapper
function initpops (){
$(".pop").each(function() {
var pop_classes = $(this).attr("class");
if ( $(this).find('.pop_menu').length) {
// do nothing
} else {
$(this).addClass("pop_menu");
$(this).wrap("<div class='"+pop_classes+"'></div>");
$(".pop_menu").attr("class", "pop_menu");
$(this).before(" \
<div class='pop_toggle'></div> \
");
}
});
}
initpops();
// assign reverse z-indexes to each pop
var totalpops = $(".pop").length + 100;
$(".pop").each(function(i) {
var popzindex = totalpops - i;
$(this).css({ zIndex: popzindex });
});
// close pops if user clicks outside of pop
activePop = null;
function closeInactivePop() {
$(".pop").each(function (i) {
if ($(this).hasClass('active') && i!=activePop) {
$(this).removeClass('active');
}
});
return false;
}
$(".pop").mouseover(function() { activePop = $(".pop").index(this); });
$(".pop").mouseout(function() { activePop = null; });
$("body").on("click", ".pop", function(){
closeInactivePop();
});
// toggle that pop
$("body").on("click", ".pop_toggle", function(){
$(this).parent(".pop").toggleClass("active");
});
}
})(jQuery);
now when i load this script on an ajax call the new pop-out menus work but the old ones do not react to the onclick event.
You shouldn't mess with the plugin. It works exactly like it should.
Better show us how you call this on elements that you already have.
Also I don't like this plugin. Better use something from JqueryUI
You can do such thing in much easier way.
[edit]
I tried your first code (the plugin) and it works correctly for me.
[edit]
OK. I get it. You call $.pop(); multiple times. You shouldn't! Calling $.pop(); will pin up the drop down menu to all elements that has class="pop". This is the reason why you have such funny stack.
Just use $.pop(); once.
Plugin doesn't give ability to connect NEW elements that was dynamically created on the page.
Removed pop from ajax call and just called this on success:
$(".pop").each(function() {
var pop_classes = $(this).attr("class");
if ( $(this).find('.pop_menu').length) {
// do nothing
} else {
$(this).addClass("pop_menu");
$(this).wrap("<div class='"+pop_classes+"'></div>");
$(".pop_menu").attr("class", "pop_menu");
$(this).before(" \
<div class='pop_toggle'></div> \
");
}
});
// assign reverse z-indexes to each pop
var totalpops = $(".pop").length + 100;
$(".pop").each(function(i) {
var popzindex = totalpops - i;
$(this).css({ zIndex: popzindex });
});
// close pops if user clicks outside of pop
activePop = null;
function closeInactivePop() {
$(".pop").each(function (i) {
if ($(this).hasClass('active') && i!=activePop) {
$(this).removeClass('active');
}
});
return false;
}
$(".pop").mouseover(function() { activePop = $(".pop").index(this); });
$(".pop").mouseout(function() { activePop = null; });

Categories