I have written some JavaScript that opens an element when an element is clicked. However I can't get the:
var menu = document.getElementById(show);
if (menuOpen && e.target !== menu){...}
This is not working to how I want it because:
You can open more than one of the showed elements when I only want one open at a time.
When I click inside the element it closes, I only want it to close if they have clicked outside the box.
function openBox(button, show){
var menuOpen = false; //to toggle when the button is clicked.
// checks the whole document for clicks and then if the element is open it will >
// check to see if you have clicked away from it or not.
document.addEventListener("click", function(e){
var menu = document.getElementById(show);
if (menuOpen && e.target !== menu){ // if elements open and the click event target does not match >
menu.style.display = "none"; // we will close it
menuOpen = false;
}
},false);
// add an event listner to the button element and then if its clicked stop any >
// links an stop bubbling and then change the display style.
document.getElementById(button).addEventListener("click", function(e){
var menu = document.getElementById(show);
e.preventDefault();
e.stopPropagation();
if (menuOpen){
menu.style.display = "none";
menuOpen = false;
} else {
menu.style.display = "block";
menuOpen = true;
}
},false);
}
openBox("signInButton", "signIn");
openBox("bagButton", "shoppingBag");
openBox("currencyButton", "currencySelect");
http://jsfiddle.net/jamcoupe/9CEGw/
Edit:
After #Felix Kling post I changed the code to:
document.addEventListener("click", function(e){
var menu = document.getElementById(show);
if (menuOpen && (e.target.parentNode !== menu) && (e.target !== menu)){
menu.className = "closedBoxes";
pointer = document.getElementById(arrow).className = "arrowE";
menuOpen = false;
}
},false);
This has solve the first problem but I am still stuck on how to make it so that only one box is ever open at one giving time. So when a user has signIn box open and clicks on currencyChanger I want the signIn box to be off.
http://jsfiddle.net/jamcoupe/kcF9Z/7/
When I click inside the element it closes, I only want it to close if they have clicked outside the box.
As I already said in my comment, if the box contains other elements, then e.target does not refer to the box itself but to the element within the box.
So in order to test whether the click was outside or not, you have to test whether e.target is an element within the box or the box itself. For that, you have to traverse the DOM tree.
Example:
var target = e.target;
while(target && target !== menu) {
target = target.parentNode;
}
if(!target) {
// click was outside of the box
}
You can open more than one of the showed elements when I only want one open at a time.
If you want to make the three dialogs dependent on each other, you have to maintain some shared state. I'd suggest, instead of having three dialogs, you can have one dialog manager which takes care of opening and closing the boxes.
Example:
function DialogManager() {
this.dialogs_ = {};
this.openedDialog_ = null;
this.init_();
}
DialogManager.prototype.init_ = function(e) {
var self = this;
document.addEventListener('click', function(e) {
var id = e.target.id;
if(id && id in self.dialogs_) { // if one of the buttons was clicked.
self.openDialog(id); // the dialog is opened (or closed)
return;
}
if(self.openedDialog_) { // if a dialog is currently open, we have to
var target = e.target; // close it if the click was outside
while(target && target.id !== self.openedDialog_) {
target = target.parentNode;
}
if(!target) {
self.closeDialog(self.openedDialog_);
}
}
}, false);
};
DialogManager.prototype.registerDialog = function(button_id, dialog_id) {
this.dialogs_[button_id] = dialog_id;
};
DialogManager.prototype.openDialog = function(id) {
var open_id = this.openedDialog_;
if(open_id) {
this.closeDialog(open_id);
}
if(id !== open_id) {
var dialog = document.getElementById(this.dialogs_[id]);
dialog.style.display = "block";
this.openedDialog_ = id;
}
};
DialogManager.prototype.closeDialog = function(id) {
var dialog = document.getElementById(this.dialogs_[id]);
dialog.style.display = "none";
this.openedDialog_ = null;
};
DEMO
I hope this gives you some idea. There is still a lot which can be improved, for example, now the manager listens to every click event, no matter whether a dialog is open or not.
Related
it is my first question on Stackoverflow after many years of reading answers here. Sorry for my english, it is not my nativelanguage.
I develop on wordpress a system to open and close the menu and open the submenu.
When I open the submenu my menu closes despite I prevent the click on a tag
let openMenu = document.querySelectorAll('.et_mobile_menu .menu-item-has-children>a');
let arrayLinkMenu = Array.from(openMenu);
for(let i = 0; i < arrayLinkMenu.length; i++){
arrayLinkMenu[i].addEventListener('click', (event) => {
arrayLinkMenu[i].nextElementSibling.style.setProperty('display', 'block', 'important');
event.preventDefault();
event.stopPropagation();
})
}
I tried with return false but I get the same issue.
When I click, it creates an a tag with # as URL. It's like this my page refresh but not... I never see my web browser refresh really my page.
I put the code who open and close the menu, we never know
/**If menu is open create div with button close menu and add interation to submenu*/
//Verify the click on the button
let buttonMenu = document.getElementsByClassName('mobile_nav');
buttonMenu[0].addEventListener('click', function(){
let menuClosed = document.getElementsByClassName('closed-menu');
if (menuClosed.length > 0){
menuClosed[0].remove();
}
//if it' open create div or a tag
if(!buttonMenu[0].classList.contains('opened')){
let aTag = document.createElement('a');
aTag.href = '#';
aTag.innerHTML = '<img src="url_image" />';
aTag.classList.add('closed-menu')
let mobileMenu = document.getElementsByClassName('et_mobile_menu');
mobileMenu[0].appendChild(aTag);
//create action to close menu
let menuClosed = document.getElementsByClassName('closed-menu');
if(menuClosed.length > 0){
let mobileNav = document.getElementsByClassName('mobile_nav')
menuClosed[0].addEventListener('click', function(event){
mobileNav[0].classList.remove('opened');
mobileNav[0].classList.add('closed');
event.preventDefault();
})
}
//click on menu with subMenu
let openMenu = document.querySelectorAll('.et_mobile_menu .menu-item-has-children>a');
let arrayLinkMenu = Array.from(openMenu);
for(let i = 0; i < arrayLinkMenu.length; i++){
arrayLinkMenu[i].addEventListener('click', (event) => {
arrayLinkMenu[i].nextElementSibling.style.setProperty('display', 'block', 'important');
event.preventDefault();
event.stopPropagation();
return false;
})
}
}else{
// take an ellement and remove it to not have many ellements on same menu
let menuClosed = document.getElementsByClassName('closed-menu');
if (menuClosed.length > 0){
menuClosed[0].remove();
}
}
})
Thanks per advance.
Try to stopImmediatePropagation() so you are sure that not even other event listeners registered on the element itself are executed.
I have some HTML tabs which use radio buttons to control them. However I am using a select drop down menu with some javascript to control them on mobile screen sizes.
// Get select element for mobile navigation
const select = document.getElementById("location");
// Event listener for selecting tabs event for mobile
select.addEventListener("change", (e) => {
const target = e.target.value;
const venueTabs = document.querySelectorAll(".tabs__radio");
for (let i = 0; i < venueTabs.length; i++) {
if (venueTabs[i].id === target) {
venueTabs[i].setAttribute("checked", "checked");
console.log(venueTabs[i], target);
} else if (venueTabs[i].id !== target) {
venueTabs[i].removeAttribute("checked");
}
}
});
My eventListener seems to work and is logging out what is expected. It compares the tab div id to the event target.
However this only seems to work when I test each select option twice, on the 3rd attempt the tabbed content disappears (css switches to display: none).
I can't seem to work out what is causing the error.
I have set up a code sandbox with my code https://codesandbox.io/s/nice-ramanujan-2yq1r?file=/src/index.js to help debug it. If the drop down menus isn't showing, you may have to view it for mobile/ below 700px wide & it'll display the select drop down menu.
Can anyone help identify what is causing this bug? I previously had hard coded the EventListener that worked perfectly, it looked like this
select.addEventListener("change", (e) => {
const target = e.target;
const tabone = document.getElementById("tab0");
const tabtwo = document.getElementById("tab1");
const tabthree = document.getElementById("tab2");
const tabfour = document.getElementById("tab3");
if (target.value === "tab0") {
tabtwo.removeAttribute("checked");
tabthree.removeAttribute("checked");
tabfour.removeAttribute("checked");
tabone.setAttribute("checked", "checked");
} else if (target.value === "tab1") {
tabthree.removeAttribute("checked");
tabfour.removeAttribute("checked");
tabone.removeAttribute("checked");
tabtwo.setAttribute("checked", "checked");
} else if (target.value === "tab2") {
tabfour.removeAttribute("checked");
tabone.removeAttribute("checked");
tabtwo.removeAttribute("checked");
tabthree.setAttribute("checked", "checked");
} else if (target.value === "tab3") {
tabone.removeAttribute("checked");
tabtwo.removeAttribute("checked");
tabthree.removeAttribute("checked");
tabfour.setAttribute("checked", "checked");
}
});
However it's not dynamic enough to take any number of tabs that may exist.
This doesn't need any looping over all the radio buttons to begin with - just select the one element you want to set checked via its id:
select.addEventListener("change", (e) => {
const target = e.target.value;
const venueTab = document.querySelector("#"+target);
venueTab.checked = true;
});
In HTML5 I have a dropdown menu . When choosing different options I hide or show different parts of my page. Here is that script:
document
.getElementById('target')
.addEventListener('change', function () {
'use strict';
var vis = document.querySelector('.vis'),
target = document.getElementById(this.value);
if (vis !== null) {
vis.className = 'inv';
}
if (target !== null ) {
target.className = 'vis';
}
});
However what I want to do now, in another script is to preload an option from the dropdown. I can do it easily with this script:
setSelectedIndex(document.getElementById('target'),'content_1');
function setSelectedIndex(s, valsearch)
{
// Loop through all the items in drop down list
for (i = 0; i< s.options.length; i++)
{
if (s.options[i].value == valsearch)
{
// Item is found. Set its property and exit
s.options[i].selected = true;
break;
}
}
return;
}
This is where my problem comes up, my dropdow will get the value I want, but the part that I want to be shown when choosing that option won't come up.
That is because change events need to happen from the browser.
When the user commits the change explicitly (e.g. by selecting a value
from a 's dropdown with a mouse click, by selecting a date
from a date picker for , by selecting a file in the
file picker for , etc.);
If your using Jquery you can:
$("#id").val("value").trigger('change');
or you can use javascript if your not worried about building the event object:
if ("createEvent" in document) {
var evt = document.createEvent("HTMLEvents");
evt.initEvent("change", false, true);
element.dispatchEvent(evt);
}
else
element.fireEvent("onchange");
I would recommend moving your anonymous onchange function into a named function that you can call once onload, and again onchange.
Here is the function I wrote:
function setContent(id) {
//get the current visible content
var vis = document.querySelector('.vis');
//get the target element by id
var target = document.getElementById(id);
//make current vis element inv
if (vis) vis.className = "inv";
//make target element vis
if (target) target.className = 'vis';
}
and a fiddle
edited: got rid of querySelectorAll to stick closer to OP original code and updated fiddle. clarified and commented code.
The problem you have is changing a vale or the selected value of an input with JavaScript does not trigger any change event. So you would need to manually trigger the event.
function setSelectedIndex(s, valsearch)
{
// Loop through all the items in drop down list
for (i = 0; i< s.options.length; i++)
{
if (s.options[i].value == valsearch)
{
// Item is found. Set its property and exit
s.options[i].selected = true;
break;
}
}
//Setting the selected value with JavaScript does not trigger the change event so you need to manually trigger the change event
if ("createEvent" in document) {
var evt = document.createEvent("HTMLEvents");
evt.initEvent("change", false, true);
s.dispatchEvent(evt);
} else {
s.fireEvent("onchange");
}
return;
}
I have a small panel which i close if mouse down button is pressed anywhere else than that panel, basically it clears the data to display and just with the help of angularjs ng-show i hide it if there is no data...application is in angularjs and jquery
please find the code below
var closeSearchResultsIfClickedOutside = function (e) {
if ($(e.target).parents('.searchResults').length === 0) {
var scope = angular.element($("#searchContainer")).scope();
scope.$apply(function () {
/*Cancels any existing search*/
if ($scope.defer != undefined) {
$scope.defer.resolve();
}
$scope.showSearchResults = false;
reinitialize();
});
$("html").off("mousedown", closeSearchResultsIfClickedOutside);
reinitializePanelsWidth();
}
};
but i dont want to close this panel if mouse down is on scrollbar of browser window or any scrollbar..please tell me how to do that
to fix the above problem i am not capturing both event, mouse down and click, if the target element on both event matches then only i am closing the panel.
/*
If mouse down and click on the same control, then only close the panel,
Click event closing is added to avoid closing panel on scrollbar click.
*/
var closeSearchResultsIfClickedOutside = function (e) {
if (e.type === 'mousedown') { /* only if mouse down is outside of search panel then only close the panel. */
if($(e.target).parents('.searchResults').length === 0)
{
isMouseDownOnSearchPanel = true;
}
else {
isMouseDownOnSearchPanel = false;
}
}
else if (e.type === 'click' && isMouseDownOnSearchPanel) { /*click event is implemented to avoid closing when mouse down is on scrollbar. you dont get click get event for scrollbar*/
var scope = angular.element($("#searchContainer")).scope();
$("html").off("mousedown", closeSearchResultsIfClickedOutside);
$("html").off("click", closeSearchResultsIfClickedOutside);
isMouseDownOnSearchPanel = false;
reinitializePanelsWidth();
}
};
I'm using twitter bootstrap to display popovers with a click event. I'm requesting the info with the click event but I want to hide the popover after it looses focus so the user isn't required to click it again. Is this possible?
Basically I want to show the popover with a click event but then when the launch point looses focus from the mouse the popover is hidden.
Here is a link to the popover doc from twitter-bootstrap: http://twitter.github.com/bootstrap/javascript.html#popovers
This is what I'm currently doing:
jQuery:
$('.knownissue').on('click', function() {
var el = $(this);
if (el.data('showissue') == 'true') {
el.popover('toggle');
el.data('showissue', 'false');
return;
}
$.post('functions/get_known_issues.php', function(data) {
if (data.st) {
el.attr('data-content', data.issue);
el.popover('toggle');
el.data('showissue', 'true');
}
}, "json");
});
Any thoughts?
The following should work.
$('.knownissue').mouseleave(function() {
$(this).popover('hide');
});
Here is a custom jQuery event I call 'clickoutside'. It gets fired if and only if you click the mouse outside of the target element. It could easily be adapted for other event types (mousemove, keydown, etc). In your case, when fired it could close your modal.
(function ($) {
var count = 0;
$.fn.clickoutside = function (handler) {
// If the source element does not have an ID, give it one, so we can reference it
var self = $(this);
var id = self.attr('id');
if (id === '') {
id = 'clickoutside' + count++;
self.attr('id', id);
}
// Watch for the event everywhere
$('html').click(function (e) {
var source = $(e.target);
// ... but, stop it from propagating if it is inside the target
// element. The result being only events outside the target
// propagate to the top.
if (source.attr('id') == id || source.parents('#' + id).length > 0) {
return;
}
handler.call(this, e);
})
};
})(jQuery);
$('#targetElement').clickoutside(function(){
});
EDIT: Example JSFiddle.