I have a drawer menu that expands on btn click, what I am trying to achieve is closing the menu when a user clicks on the sidenav-body class which covers the whole body.
Here is the base html and js
<div class="offcanvas-body sidenav-body">
<main id="main-content" role="main">
<div class="container-fluid short-section-row">
<div class="row">
<div class="side-nav-btn navbar-btn js-side-nav-btn" aria-expanded="false" aria-label="Open Side Navigation" aria-controls="SecondaryMenu">Explore This Section <span class="svg-sprite -hamburger"><svg role="img" aria-label="[object Object]"><use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#hamburger"></use></svg></span></div>
<nav class="side-nav col-sm-2" role="Secondary navigation" aria-label="Side Navigation" aria-hidden="true" id="SecondaryMenu">
<div class="side-nav__control-bar">
<button class="navbar-btn js-side-nav-btn btn btn-primary pull-right" aria-expanded="false" aria-label="Close Side Navigation" aria-controls="SecondaryMenu" tabindex="-1"><span class="svg-sprite -close"><svg role="img" aria-label="close secondary nav"><use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#close"></use></svg></span> Menu</button>
</div>
// some ul and li items
</nav>
</div>
</div>
</main>
</div>
How I defined classes
this.$body = $(el);
this.$sideNavBody = $('.sidenav-body');
this.$sideNav = $('.side-nav');
this.$controls = $('.side-nav button');
this.$sideNavBtn = $('.js-side-nav-btn');
I have a toggle function on btn click
sideNavBodyToggleEvent(){
// if the nav is open, run close event
if(this.$body.hasClass('side-is-open')) {
this.sideNavBodyCloseEvent();
} else {
this.sideNavBodyOpenEvent();
}
}
And those conditional functions are defined like so
sideNavBodyCloseEvent () {
this.$body.removeClass('side-is-open');
// always clear the 'opened state' of any open menus
this.$sideNavSection.removeClass('side-is-open');
$(this.$controls).attr('tabindex', '-1');
$(this.$sideNav).attr('aria-hidden', 'true');
$(this.$sideNavBtn).attr('aria-expanded', 'false');
$(this.$sideNavSectionToggle).removeClass('side-is-open');
// unbind the pushed body click event
this.$sideNavBody.off();
}
sideNavBodyOpenEvent() {
this.$body.addClass('side-is-open');
$(this.$sideNav).attr('aria-hidden', 'false');
$(this.$controls).removeAttr('tabindex');
$(this.$sideNavBtn).attr('aria-expanded', 'true');
// bind an event on the div containing the pushed body
// original code left by prev dev was this.$sideNavBody.offClick.bind(this) and doesnt work as I think its trying to run both functions at once (the menu doesnt even open);
//below I am just trying to test if the click event even makes it to this.$.sideNavBody which is the .sidenav-body class and the section I want users to be able to click to close
$(this.$sideNavBody).click(function(e){
console.log(e);
});
}
The open function works and the drawer menu slides out but my attempt at closing it was as follows
$(this.$sideNavBody).click(function(e){
$(this.sideNavBodyCloseEvent());
console.log(e);
});
Which returns this error Uncaught TypeError: this.sideNavBodyCloseEvent is not a function everytime the .sidenav-body / $sideNavBody is clicked
How can I pass is this sideNavBodyCloseEvent() function on that element click?
When adding the bit of code to close the menu when click on the .sidenav body the menu closes when it encounters this code from jquery
if ( !( eventHandle = elemData.handle ) ) { eventHandle = elemData.handle = function( e ) { // Discard the second event of a jQuery.event.trigger() and // when an event is called after a page has unloaded return typeof jQuery !== "undefined" && jQuery.event.triggered !== e.type ? jQuery.event.dispatch.apply( elem, arguments ) : undefined; }; }
I have never seen or had this problem before any suggestions?
Does this work?
$(this.$sideNavBody).click(function(e){
$(this.sideNavBodyCloseEvent());
console.log(e);
}.bind(this));
The inner function has its own this object, which doesn't have the sideNavBodyCloseEvent method. To use the this object of the outer function in the inner function, use bind.
Normally, you have an initialisation function that binds the necessary event handlers:
init () {
this.$sideNavBtn.click(this.sideNavBodyOpenEvent.bind(this));
this.$sideNavBody.click(this.sideNavBodyCloseEvent.bind(this));
}
How about doing this a different way.
I think I've made a mistake somewhere in there with the elements, but passing this element as a parameter is the main change.
$body = $(el);
$sideNavBody = $('.sidenav-body');
$sideNav = $('.side-nav');
$controls = $('.side-nav button');
$sideNavBtn = $('.js-side-nav-btn');
$sideNavBody.click(function(e){
sideNavBodyCloseEvent($(this))
console.log(e);
});
sideNavBodyCloseEvent (element) {
element.$body.removeClass('side-is-open');
// always clear the 'opened state' of any open menus
element.$sideNavSection.removeClass('side-is-open');
$controls.attr('tabindex', '-1');
$sideNav.attr('aria-hidden', 'true');
$sideNavBtn.attr('aria-expanded', 'false');
$sideNavSectionToggle.removeClass('side-is-open');
// unbind the pushed body click event
$sideNavBody.off();
}
Related
Here's a pen with the full html: https://codepen.io/froggomad/pen/WLdzoB
I'm writing 2 functions - one to show hidden content, and one to hide it. I'm wanting the show() function to execute on the parent div and the hide() function to execute on the div with the selector .click-text.
However, I'm switching text on .click-text from show to hide so I don't want the hide function to remain on the text at all times. I also want it obvious that its interactive text when changing to a hide function, so I make it a link.
That's all well, but when attempting to set the onclick Attr of the parent back to the show() function, nothing in the hide block executes at all.
If I remove the line setting the parent's onclick Attr, the script executes as expected. If I set another element's onclick Attr, the script executes as expected.
However, with that line in there, nothing happens and there's no output in the console to indicate an error. I even set an alert with the type of element and classname to ensure I'm targeting the right element.
Get closest parent of element matching selector:
var getClosest = function (element, selector) {
for ( ; element && element !== document; element = element.parentNode ) {
if ( element.matches(selector) ) return element;
}
return null;
}
Show Hidden Element ul.service-category-menu
function show(elem) {
var menu = elem.querySelector("ul.service-category-menu"),
click = elem.querySelector(".click-text"),
parent = getClosest(elem, '.service-category');
;
if (menu.style.display === "none" || menu.style.display === "") {
menu.style.display = "block";
click.innerHTML = "<a href=\"#\">Click to Hide<\/a>";
click.setAttribute('onclick','hide(this);');
elem.setAttribute('onclick', 'null');
}
}
Hide Element
function hide(elem) {
var parent = getClosest(elem, '.service-category'),
menu = parent.querySelector("ul.service-category-menu"),
click = parent.querySelector(".click-text")
;
alert(parent + "\n" + parent.className);
//Outputs div element with expected class name (class name is unique on each div)
if (menu.style.display === "block") {
menu.style.display = "none";
click.innerHTML = "Click to Show";
click.setAttribute('onclick', 'null');
//the above lines don't execute when the following line is in place. There's no error in console.
parent.setAttribute('onclick','show(this)');
}
}
First off, I must confess that I'm against using onclick attributes. If you're not using a framework such as VueJS or React, I think HTML and JS should remain separated for better control and maintainability.
You can use addEventListener, removeEventListener, and e.stopPropagation() to avoid triggering multiple event handlers.
Events have two phases:
Event capture: the event spreads from the document all the way down to the target element.
To catch an event during this phase, do:
elm.addEventListener('click', myFunc, true);
Event bubbling: the event bounces back from the target to the document.
To catch an event during this phase, do:
elm.addEventListener('click', myFunc, false); /* or just omit the 3rd param */
Using e.stopPropagation() allows you to break that chain.
// When the DOM is ready
window.addEventListener("DOMContentLoaded", init);
function init() {
// Get all categories
var $categories = document.querySelectorAll(".service-category");
// For each of them
Array.from($categories).forEach(function($category) {
// Add an event listener for clicks
$category.addEventListener("click", show);
});
}
function getClosest(element, selector) {
for (; element && element !== document; element = element.parentNode) {
if (element.matches(selector)) return element;
}
return null;
}
function show(e) {
var $menu = this.querySelector("ul.service-category-menu"),
$click = this.querySelector(".click-text");
if (["none", ""].includes($menu.style.display)) {
$menu.style.display = "block";
$click.innerHTML = 'Click to Hide';
$click.addEventListener("click", hide);
// Remove the `show` event listener
this.removeEventListener("click", show);
}
e.stopPropagation();
}
function hide(e) {
var $parent = getClosest(this, ".service-category"),
$menu = $parent.querySelector("ul.service-category-menu"),
$click = $parent.querySelector(".click-text");
if (!["none", ""].includes($menu.style.display)) {
$menu.style.display = "none";
$click.innerHTML = "Click to Show";
$click.removeEventListener("click", hide);
$parent.addEventListener("click", show);
}
e.stopPropagation();
}
.service-category{display:inline-block;border:3px solid #ccc;margin:1%;font-weight:700;font-size:3.5vw;cursor:pointer;background-color:#fff;z-index:3;background-position:center;background-size:cover;color:#000}.click-text{text-align:right;font-size:1.25vw;font-style:italic;font-weight:700;padding-right:1%}.service-category:hover .click-text{color:#b22222}.service-category-menu{display:none;margin-left:8%;margin-right:8%;margin-top:1%;background-color:#fff;font-weight:700;font-size:1.6vw;border-radius:10px}
<div class="service-category web-back" id="web-back">
<div class="row-overlay">
Web <br /> Development
<div class="click-text">Click to Show</div>
<ul class="service-category-menu web">
<li>
Some text...
</li>
</ul>
</div>
</div>
<div class="service-category web-front" id="web-front">
<div class="row-overlay">
Web <br /> Design
<div class="click-text">Click to Show</div>
<ul class="service-category-menu web">
<li>
Some text...
</li>
</ul>
</div>
</div>
It is executed, it's just after you click that Click to Hide, the event continues to parent and the event handler of the parent executed. Thus, what exactly happen is (with that line), after hide() called, you inadvertently called show().
In javascript it's usually called bubbles (when you click the children, the click handler of parent will also be executed after click handler of children complete).
So the solution, you can add this line at the end of the hide() function
event.stopPropagation();
To stop the event from continuing to the parent
Setting event.stopPropagation as mentioned in the other answer will potentially fix your issue. Alternatively, you can change the last line of your hide function to window.setTimeout(e => parent.setAttribute('onclick','show(this)'), 0).
What's happening right now is:
You click
it executes your hide function, and during that function it binds a click event to the parent
The click propagates to the parent and executes the newly bound function, re-showing the content.
By using setTimeout(fn, 0), you're making sure the click event completes before the function is bound to the parent.
This should be pretty simple but for some reason it isn't working, I'm getting the proper console.logs at the right time, but the focus isn't going to the correct place, please refer to my jsfiddle
https://jsfiddle.net/bqt0np9d/
function checkTabPress(e) {
"use strict";
// pick passed event of global event object
e = e || event;
if (e.keyCode === 9) {
if (e.shiftKey) {
console.log('back tab pressed');
firstItem.onblur=function(){
console.log('last a focus left');
lastItem.focus();
};
e.preventDefault();
}
console.log('tab pressed');
lastItem.onblur=function(){
console.log('last a focus left');
firstItem.focus();
};
e.preventDefault();
}
}
modal.addEventListener('keyup', checkTabPress);
I had to lock focus within a modal that we had used within a React component.
I added eventListner for KEY DOWN and collected Tab and Shift+Tab
class Modal extends Component {
componentDidMount() {
window.addEventListener("keyup", this.handleKeyUp, false);
window.addEventListener("keydown", this.handleKeyDown, false);
}
componentWillUnmount() {
window.removeEventListener("keyup", this.handleKeyUp, false);
window.removeEventListener("keydown", this.handleKeyDown, false);
}
handleKeyDown = (e) => {
//Fetch node list from which required elements could be grabbed as needed.
const modal = document.getElementById("modal_parent");
const tags = [...modal.querySelectorAll('select, input, textarea, button, a, li')].filter(e1 => window.getComputedStyle(e1).getPropertyValue('display') === 'block');
const focusable = modal.querySelectorAll('button, [href], input, select, textarea, li, a,[tabindex]:not([tabindex="-1"])');
const firstFocusable = focusable[0];
const lastFocusable = focusable[focusable.length - 1];
if (e.ctrlKey || e.altKey) {
return;
}
const keys = {
9: () => { //9 = TAB
if (e.shiftKey && e.target === firstFocusable) {
lastFocusable.focus();
}
if (e.target === lastFocusable) {
firstFocusable.focus();
}
}
};
if (keys[e.keyCode]) {
keys[e.keyCode]();
}
}
}
One of the problems is that you are using keyup instead of keydown. The keyup will only fire after the tab has already fired. However, making that change to your code results in the keyboard being trapped on one of the links. The code is flawed.
Here is some code that does what you want (using jQuery)
http://dylanb.github.io/javascripts/periodic-1.1.js
// Add keyboard handling for TAB circling
$modal.on('keydown', function (e) {
var cancel = false;
if (e.ctrlKey || e.metaKey || e.altKey) {
return;
}
switch(e.which) {
case 27: // ESC
$modal.hide();
lastfocus.focus();
cancel = true;
break;
case 9: // TAB
if (e.shiftKey) {
if (e.target === links[0]) {
links[links.length - 1].focus();
cancel = true;
}
} else {
if (e.target === links[links.length - 1]) {
links[0].focus();
cancel = true;
}
}
break;
}
if (cancel) {
e.preventDefault();
}
});
You can see a working version of this dialog here
http://dylanb.github.io/periodic-aria11-attributes.html
Click the text in one of the colored boxes to see the dialog pop up.
The e.preventDefault() has no effect on the keyup event (as the default browser action has already been fired)
Despite this, your example works. But only if there are links before and after the modal
If you change your HTML code with the following, adding one link before and one link after the modal; you will see that your focus is trapped in the modal:
other link
<div id="modal">
Link One
Link Two
</div>
other link
That's because there is no default browser action in such case, and then no action to prevent.
Trapping focus within a modal is very hard to do it on your own. If you're able to install third-party dependencies in your project, you can use the focus-trap package.
You can easily trap focus to any component with vanilla Javascript;
import { createFocusTrap } from 'focus-trap'
const modal = document.getElementById('modal')
const focusTrap = createFocusTrap('#modal', {
onActivate: function () {
modal.className = 'trap is-visible'
},
onDeactivate: function () {
modal.className = 'trap'
},
})
document.getElementById('show').addEventListener('click', function () {
focusTrap.activate()
})
document.getElementById('hide').addEventListener('click', function () {
focusTrap.deactivate()
})
or even React;
import React from 'react'
import ReactDOM from 'react-dom'
// Use the wrapper package of `focus-trap` to use with React.
import FocusTrap from 'focus-trap-react'
const Demo = () => {
const [showModal, setShowModal] = React.useState(false)
return (
<div>
<button onClick={() => setShowModal(true)}>show modal</button>
<FocusTrap active={showModal}>
<div id="modal">
Modal with with some{' '}
focusable elements.
<button onClick={() => setShowModal(false)}>
hide modal
</button>
</div>
</FocusTrap>
</div>
)
}
ReactDOM.render(<Demo />, document.getElementById('demo'))
I did a small write-up about the package here, which explains how to use it with either vanilla Javascript or React.
I thought I had solved trapping the focus on a modal by using tab, shift+tab, and arrow keys detection on keyup and keydown, focus, focusin, focusout on the first and last focusable elements inside the modal and a focus event for the window to set the focus back on the first focusable element on the form in case the focus "escaped" the modal or for situations like jumping from the address bar to the document using tab, but something weird happened. I had activated "Caret Browsing" in one of my browsers accidently, and that's when I realized all methods to trap focus failed miserably. I personally went on a rabbit whole to solve this for a modal. I tried focusin, focusout on the modal, matching focus-within pseudo classes, {capture: true} on the focus event from the modal and window, nothing worked.
This is how I solved it.
I recreated the modal to have a different structure. For the sake of simplicity, I am omitting a lot of things, like the aria attributes, classes, how to get all focusable elements, etc.
<component-name>
#shadow-root (closed)
<div class="wrapper">
<div class="backdrop"></div>
<div class="window>
<div tabindex="0" class="trap-focus-top"> </div>
<div class="content">
<div class="controls"><!-- Close button, whatever --></div>
<header><slot name="header"></slot></header>
<div class="body"><slot></slot></div>
<footer><slot name="footer"></slot></footer>
</div>
<div tabindex="0" class="trap-focus-bottom"> </div>
</div>
</div>
</component-name>
Search the contents div for focusable elements, to save the first and last one. If you find only one then that one will be first and last. If you find zero, then set the div for the body (.body) tabindex to "0" so that you have at least one element to set the focus on.
Before and after the content div we have two focusable divs, trap-focus-top and trap-focus-bottom, the first one when getting focus will jump the focus to the last focusable element detected on step one, and the second one will jump the focus to the first focusable element detected on step one. No need to capture any key events, just focus event on these elements. If you notice the non-breaking space on trap-focus elements, this is for mimicking content, because I noticed that the arrow keys went through these elements without firing any events when empty. When I realized this I added some content and everything worked, so I added a non-breaking space and styled the elements so that they do not occupy any space.
Capture all focus events from the window with the use capture flag set to true, so that every focus event whose target was different to the component (focus events inside the shadow-root wont't be captured with the actual target but the component itself) will result in the focus being set on the modal elements again.
Now there's another problem, let's say there's zero focusable elements on your modal outside of any controls, like a button to close the modal, then we set tabindex to 0 on the modal's body, your focus should go from the close button to the modal's body and vice versa, now, the caret browsing won't work on the content because the div.body will have the focus, not the actual content. This means I have to create another function that places the cursor at the beginning of the content whenever the body receives the focus.
startCursor = () => {
/* componentbody is a placeholder for the element with the actual content */
let text = componentbody.childNodes[0];
if (text) {
let range = new Range();
let selection = document.getSelection();
range.setStart(text, 0);
range.setEnd(text, 0);
selection.removeAllRanges();
selection.addRange(range);
componentbody.scrollTop = 0;/* In case the body has a scrollbar */
}
}
For anyone out there, this is what worked for me.
I have a popover which I am calling when an event is fire. But I need that popover to be global
here the popover
<div class="popover" tabindex="-1">
<h5 ng-bind-html="title" ng-show="title"></h5>
...
<button type="button" ng-click="refreshAfterBet(); $hide()">
OK
</button>
</div>
</div>
I am calling it like this
<button ng-click="placeStraightBet(slip)"
title="Bet Confirmation"
data-placement="bottom" data-template="views/betConfirmModal.html"
data-auto-close="1"
bs-popover="" ng-disabled="slip.active != '1'"> Place Bet
</button>
and here the Angular part
$scope.refreshAfterBet = function() {
BetSlipFactory.retrieveBetSlip();
};
$scope.placeStraightBet = function(slip) {
var winValue = parseFloat(slip.risk, 10),
riskValue = parseFloat(slip.win, 10),
riskWin;
if (winValue && riskValue && riskValue > 4) {
BetSlipFactory.placeQuickBet({
wagerType: 1
}).then(function(betId) {
// HERE I NEED TO CALL THE POPOVER
$scope.betId = betId;
}, function(err) {
$scope.betPlaceErr = err.message;
console.log('Whoops, your bet was not placed', err.message);
});
}
};
OK, what I need:
1 - eliminate the function refreshAfterBet
the reason why I need to remove it is because that function should be within the promise where I wrote // HERE I NEED TO CALL THE POPOVER like this:
...
}).then(function(betId) {
// HERE I NEED TO CALL THE POPOVER
BetSlipFactory.retrieveBetSlip().then(function(){
$scope.betId = betId;
});
} ...
If I put it there as you see above, the popover just disappear so the user can not see it. I attached the function refreshAfterBet to the button in the popover, but I am wrong, that function should be call when you call placeStraightBet.
here the DOCS for the popover I am using
So how should I call this popover ?
I am creating my simple jQuery plugin that can be use to attach for any action's confirmation. But facing very strange issue, It work fine for single element click, But when i am going to click for second element which also bind with my plugin it work fine but it's also fires for previous clicked element as well.
(function ($) {
$.fn.BootConfirm = function (options) {
// Establish our default settings
var settings = $.extend({
message: 'Are you sure about this ?',
complete: null
}, options);
var self = this;
var cointainer = '\
<div class="modal fade" id="confirmBoot" role="dialog" aria-labelledby="confirmDeleteLabel" aria-hidden="true">\
<div class="modal-dialog">\
<div class="modal-content">\
<div class="modal-header">\
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>\
<h4 class="modal-title">Confirm action</h4>\
</div>\
<div class="modal-body">\
<p>Are you sure about this ?</p>\
</div>\
<div class="modal-footer">\
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>\
<button type="button" class="btn btn-success btn-ok" id="confirm">Ok</button>\
</div>\
</div>\
</div>\
</div>';
return this.each(function () {
var self = this;
$(this).click(function () {
if (!$('#confirmBoot').length) {
$('body').append(cointainer);
}
if (settings.message) {
$('#confirmBoot').find('.modal-body').text($(this).attr('data-confirm'));
}
$('#confirmBoot').modal({ show: true });
if ($.isFunction(settings.complete)) {
$('#confirmBoot').find('.btn-ok').click(function () {
$.when(settings.complete.call(this, self)).done(function () {
$('#confirmBoot').modal("hide"); // Alerts "123"
});
});
}
});
});
}
}(jQuery));
This is my callback function :
function kaushik(myObject) {
ManageAcriveProducts(myObject);
};
and i am calling it by following way
$('a[data-confirm]').BootConfirm({
complete: kaushik
});
For more detail check this js fidder Jsfiddle. Can anyone one share possible solution or better way to do this. Or is there any better way to achieve this ?
The problem is that you're assigning a click on your btn-ok on every click event on a bootconfirmed object. And each click is linked to the object that has been clicked, so it ends up in your callback every time you click btn-ok.
One simple fix, though I'm not sure it's the best, is to remove the click on your btn-ok after the action is complete. Like this:
$.when(settings.complete.call(this, self)).done(function () {
$('#confirmBoot').modal("hide");
$('#confirmBoot').find('.btn-ok').off('click');
});
Working fiddle: http://jsfiddle.net/ywunutyw/
EDIT:
A little improvement on previous solution, it might need some adjustments, since I didn't look into details, but it should give you some ideas. To prevent adding click events and removing them every time user clicks on a button, you can define the click on modal window outside click behavior of each active/inactive button. And on click of active/inactive you define target that will be used in modal confirmation. Like this:
Just before calling behaviors with this.each:
$(document).on('click', '#confirmBoot .btn-ok',
function (e) {
if ($.isFunction(settings.complete)) {
console.log(self)
$.when(settings.complete.call(this, click_origin)).done(function () {
$('#confirmBoot').modal("hide");
});
}
});
Then on the click event of you active/inactive:
click_origin = e.target;
See fiddle: http://jsfiddle.net/ywunutyw/1/
I'm having a problem in my code where I click a DOM-element using JavaScript. The click does not work and I am almost sure it is no dumb programming mistake (always dangerous to say).
After deleting some DOM-elements I want my code to click an element and trigger its onclick event. However this doesn't work. According to my code the event triggers but the event doesn't happen and the click event returns the jQuery object.
HTML:
<div class="castor-tabs">
<div class="castor-tab" data-path="../SiteBuilding/alertbox.js" data-saved="saved" data-editor="5475c897f1900editor">
<span class="castor-filename">alertbox.js</span>
<span class="castor-close"><i class="fa fa-fw fa-times"></i></span>
</div>
<div class="castor-tab" data-path="../SiteBuilding/index.php" data-saved="saved" data-editor="5475c89903e70editor">
<span class="castor-filename">index.php</span>
<span class="castor-close"><i class="fa fa-fw fa-times"></i></span>
</div>
<div class="castor-tab active" data-path="../SiteBuilding/makesite.php" data-saved="saved" data-editor="5475c8997ac77editor">
<span class="castor-filename">makesite.php</span>
<span class="castor-close"><i class="fa fa-fw fa-times"></i></span>
</div>
</div>
JavaScript:
$(".castor-tabs").on("click", ".castor-close", function() {
var tab = $(this).parent();
if(tab.attr("data-saved") == "saved") {
// File is saved
if($(".castor-tab").length > 1) {
// 1 element is 'tab' the other is a second tab
if(tab.next().length > 0) {
// If element is to the right
window.newTab = tab.next();
} else if(tab.prev().length > 0) {
// If element is to the left
window.newTab = tab.prev();
}
} else {
window.newTab = false;
}
var editor = tab.attr("data-editor");
$("#" + editor).remove(); // textarea linked to CodeMirror
$("#" + editor + "editor").remove(); // Huge CodeMirror-element
tab.remove();
if(window.newTab) {
console.log("window.newTab.click()");
console.log(window.newTab.click()); // Simulate click()
}
} else {
// File isn't saved
}
});
The onclick event:
$(".castor-tabs").on("click", ".castor-tab", function() {
$(".castor-tab.active").removeClass("active");
$(this).addClass("active");
var editor = $(this).attr("data-editor");
$(".CodeMirror").hide();
$("#" + editor).show();
});
I saved the element in the window object for a reason. After the code runs and it skips the click-part I still have the DOM-element i want to click saved in the window object. This means I can run
console.log(window.newTab.click());
again. Surprisingly this does click the element and this does activate the click-event. It also returns the DOM-element instead of the jQuery-object.
The image shows in the first two lines the failed click. The third line is my manual input and the fourth line is the successful return value of the click().
I hope you can help me to solve this.
UPDATE
.trigger("click") unfortunately gives the same output..
UPDATE 2
To help you i made the website available on a subdomain. I know many of you hate it if you have to go to a different page but I hope you'll forgive me because in my opinion this cant be solved through JSFiddle.
The link is http://castor.marknijboer.nl.
After clicking some pages to open try closing them and you'll see what i mean.
try adding return false; at the end, when binding .castor-close click event
$(".castor-tabs").on("click", ".castor-close", function() {
var tab = $(this).parent();
if(tab.attr("data-saved") == "saved") {
// File is saved
if($(".castor-tab").length > 1) {
// 1 element is 'tab' the other is a second tab
if(tab.next().length > 0) {
// If element is to the right
window.newTab = tab.next();
} else if(tab.prev().length > 0) {
// If element is to the left
window.newTab = tab.prev();
}
} else {
window.newTab = false;
}
var editor = tab.attr("data-editor");
$("#" + editor).remove(); // textarea linked to CodeMirror
$("#" + editor + "editor").remove(); // Huge CodeMirror-element
tab.remove();
if(window.newTab) {
console.log("window.newTab.click()");
console.log(window.newTab.click()); // Simulate click()
}
} else {
// File isn't saved
}
return false;
});
Instead of simulating a click, why not just pull the click logic out of your click function and just call the javascript function using the arguments that you'll need to perform your business logic?
Your code is working but not able to trigger click event binded to castor-close because i tag is empty. Put some text in it and check
<span class="castor-close"><i class="fa fa-fw fa-times">Click me</i></span>
DEMO
Your click handler has the requirement that the click target should have the class castor-close but when you are calling click via JavaScript you are clicking the parent element instead (.castor-tab), and the click handler doesn't react.