How can I reattach the current JavaScript event to a different element? - javascript

I'm attempting to intercept a JavaScript event, and preventDefault, to display a warning modal. If the user chooses to accept the warning, then they click 'Continue' and carry on doing what they were doing. There are multiple possible events that this could occur for though, so I thought the best way to handle it would be to just attach the current event to the 'Continue' button.
This is the code I have so far:
$(document).on('click', 'nav.pagination a', function(e) {
e.preventDefault;
var checkedBoxes = $('.checkbox-pagination-warning:checked');
if(checkedBoxes.length > 0) {
$('.checked-box-warning-modal').modal("open");
// This is where I would attach the event to this button:
//
// $('.checked-box-warning-modal a.continue')
} else {
$(this).unbind("click");
}
})
For context, I am warning the user that there are checked checkboxes on the page before they navigate or paginate away.
Also the pagination is handled by Stimulus.
This is an example of the HTML:
<nav class="pagination">
<a href="/entities/1?signatories_page=1" class="pagination-first-page" data-action="click->entities--add-existing-signatories#onPaginate">
1
</a>
<span class="pagination-current-page">
2
</span>
</nav>
So how can I attach the event, which I ran preventDefault on, to the 'Continue' button, so that the event continues as if nothing happened?

I noticed you have an error when defining the data-action here:
<a href="/entities/1?signatories_page=1" class="pagination-first-page"click->entities--add-existing-signatories#onPaginate">
1
[update]
So I think you could prevent the paginate link action with event.preventDefault(). Then, you would trigger a global event that you would capture in a top controller. You would pass the current pagination link (event.target) to this new event that you will capture in the new controller.
Something like:
<!-- this controller will handle showing the modal when asked -->
<div data-controller="main" data-action="recheck#document->main#checkWithModal">
<!-- this is the controller that captures the click on pagination links -->
<div data-controller="pagination"></div>
</div>
----- javascript
You would use this to dispatch the event
document.dispatchEvent(new Event('recheck', {bubbles: true, detail: { existing event target here }}))

Related

Session Storage Activating All the Time

On my page I have an alert. The desired behavior is that when a user clicks the close button, the element will disappear and a key in the browser's Session Storage will be activated to prevent it from reappearing.
What's happening, however, is that the Session Storage key is placed at all times - regardless if the user clicks the close button. How might I go about having it only placed when the user clicks the button?
<script type="text/javascript">
document.addEventListener("DOMContentLoaded", function() {
let dismissed = sessionStorage.getItem("dismissed");
let alertDiv = document.getElementById("alert");
let dismissButton = document.getElementById("dismiss");
if (!dismissed) {
alertDiv.classList.remove("off");
}
dismissButton.addEventListener("click", function() {
alertDiv.classList.add("hide");
});
alertDiv.addEventListener("transitionend", function({
target
}) {
if (target.classList.contains("hide")) {
target.classList.add("off");
}
sessionStorage.setItem("dismissed", true);
});
});
</script>
<div class="ddAlert off" id="alert">
<span class="ddAlertBtn" id="dismiss">×</span>
<h5>Text</h5>
<p>Text</p>
<a class="ddBtn black" href="#" target="_blank">Button</a>
</div>
Per the transitioned event specifications the event will fire whenever a CSS transition completes on the event target. I can't tell from your code but you probably have a transition that's happening when the alert div is loaded.
Either way; you should probably set the sessionStorage key inside the click event handler rather than the transitioned handler. That way you can make sure it's only set on the click like you're wanting.

How to register to the onClick event of a Close button element

I am using a UI Action in serviceNow to call a GlildeModalForm popup window.
That window has a X(close button) that I need to catch when the user press that button. I do not have any feedback on that button click and need to catch it
I have seen some exemple on ServiceNow communty where you can register to some callback function but it is used only after submitting the form.
var modalForm = new GlideModalForm('Create User Member' , tableName );
modalForm.setOnloadCallback(formOnLoadCallback);
modalForm.setCompletionCallback(formAfterSubmitCallback);
modalForm.render();
function formOnLoadCallback() {
//Access GlideModal g_form to set field for the new record
var d_form = window.frames["dialog_frame"].g_form;
d_form.setValue('field', g_form.getValue('field'));
d_form.setValue('field', g_form.getValue('field'));
}
function formAfterSubmitCallback(action_verb, sys_id, table, displayValue) {
//Get the newly created record sys_id and then set e.g a value to the starting record
g_form.setValue('field', sys_id);
//Save the record
g_form.save();
}
By using the element inspector of my browser i can see that the Close button is define as below :
<button data-dismiss="GlideModal" class="btn btn-icon close icon-cross"
id="x_58872_needit_cascadesequence_closemodal"> <span
class="sr-only">Close</span>
</button>
I would need to find that button from the script above and then register to its onClick, then in onClick even I would like to simply send an alert of potential loosing data message
Thanks for help on this
Since click events bubble up the DOM, you can simply register a click listener on the document. The event object is automatically passed to the handler function you pass to addEventListener(). Inside the handler, you can check if the click event was raised on an element that has data-dismiss="GlideModal":
document.addEventListener('click', function(e) {
if (e.target.matches('[data-dismiss="GlideModal"]') {
alert ('Close modal clicked');
}
})
This is called a delegate event listener because the listener is not registered on the element you expect the event to occur on, but higher up the DOM tree. This also has the advantage that it works for dynamically created elements (like supposedly your modal HTML).
Edit: I just double-checked the HTML you've shown. Possibly you might have to check whether the click originates from the span inside the button, rather than the button itself:
document.addEventListener('click', function(e) {
if (e.target.matches('[data-dismiss="GlideModal"] span') {
alert ('Close modal clicked');
}
})
The SerivceNow GlideModalForm extends GlideModal which supports event registration via the on method. There are 2 supported events that might work for what you want:
closeconfirm: called by the destroy method, can be used to abort destruction
beforeclose: called once the window has already been hidden
I think you can just use the closeconfirm event to know when the close happens, you don't necessarily need to care about aborting the action, and it'll fire as soon as the user clicks the button, but before the window is removed.
Notably, however, this will probably also fire when the user clicks the "Close" button (not the X) which optionally shows via the modal preference: myGlideModal.setPreference('sysparm_button_close', 'Close')
Here's a rough example:
var myGlideModal = new GlideModal(...);
dd.on("beforeclose", function (){ /* do stuff, maybe return false to cancel */ });
You can create an onclick function as follows:
html:
<button data-dismiss="GlideModal" class="btn btn-icon close icon-cross"
id="x_58872_needit_cascadesequence_closemodal" onclick="myFunction()">
<span class="sr-only">Close</span>
</button>
JavaScript:
function myFunction() {
alert("I am an alert box!");
}

Firing a HTML attribute using angularjs without using a button

I have a HTML & javascript custom made attribute that will show a popup containing message when a button is clicked.
Button to click :
<a data-deploy-menu="menu-sheet-tutorial-1" href="#">Tap Here to open</a>
The sheet/popup that opens :
<div id="menu-sheet-tutorial-1" class="menu-wrapper">
<div class="content">
<h4>Hello, I'm action sheet!</h4>
Close
</div>
</div>
The data-deploy-menu="menu-sheet-tutorial-1" is what firing the event to open the sheet.
And, this is what happens when the button is clicked in javascript.
$('a[data-deploy-menu]').on( "click", function(){
var menu_ident = $(this).data('deploy-menu');
$('#'+menu_ident).addClass('active-menu');
$('.fade-bg').addClass('active-fade'));
});
And, this is what happens when the close button is clicked
$('.close-menu').on('click', function(){
$('.menu-wrapper').removeClass('active-menu'));
$('.fade-bg').removeClass('active-fade'));
});
The problem i'm facing is that I'm not able to fire this event without a button click.
So, using angularJS, I want to fire this sheet automatically when the form is submitted or when a particular condition is true.
AngularJS has a directive calls ngSubmit who could be provided with an expression like a function. Here is te documentation:
https://docs.angularjs.org/api/ng/directive/ngSubmit

How to enable click functionality of div in javascript or jquery?

I disabled the div by using following code
$("#menuDiv1").children().bind('click', function(){
return false;
});
menuDiv contains
<ul>
<li><a class="home" href="#/Dashboard">Dashboard</a></li>
<li><a class="forms" href="#/ViewLeads">Lead</a></li>
<li><a class="forms" href="#/ViewCases/0">Cases</a></li>
</ul>
but now I want to enable it again. How can I do that?
And is there any other solution for the same in angular js?
you can use unbind to remove disable click event
$("#menuDiv1").children().unbind('click');
To re enable click event use below code.
function clickfunc(){
// your code
}
$("#menuDiv1").children().bind('click',clickfunc);
Second option
you can use off to remove disable click event
$("#menuDiv1").children().off('click');
To re enable click event use below code.
function clickfunc(){
// your code
}
$("#menuDiv1").children().on('click',clickfunc);
EDIT as discussed below code is avoid click event on a,button tags.
DEMO
function clickfunc(event){
if(event.target.nodeName === 'A' || event.target.nodeName == "BUTTON"){
return false;
}
// your code
alert("click")
}
$("#menuDiv1").children().on('click',clickfunc);
You can use unbind to disable and bind to enable again.
$("#menuDiv1").children().unbind('click');
To bind it again
$("#menuDiv1").children().bind('click', clickhander);
You better look to use jQuery.on and jQuery.off instead of bind and unbind.
In Angular you can use ng-click and ng-disabled in combination. For example:
<button ng-click="clicked()" ng-disabled="val">Click</button>
And in controller set the val to true or false to enable/disable click on the button.
$scope.val = true; //To disable button
EDIT:
For div you can do it like this:
<div ng-click="val || clicked()" ng-disabled="val">Click</div>
using on/off you can enable or disable click event
Disable click
$("#menuDiv1").children().off('click');
Enable Click
$("#menuDiv1").children().on('click', function(){
// do here work
});
You would either unbind previously bound click event handler, or prevent event conditionally:
$("#menuDiv1").children().bind('click', function (e) {
if ($(this).hasClass('no-click')) {
e.stopPropagation();
}
});
Btw, it's better to use e.stopPropagation(); explicitly to prevent child-to-parent event bubbling, rather than implicit return false.
Based on the new information in your question, the proper way to do this in AngularJS is to use the $locationChangeStart event.
Plunker
app.run(function ($rootScope, $location) {
$rootScope.$on('$locationChangeStart', function (event, next, current) {
if ($rootScope.menuDeactivated) {
event.preventDefault();
}
});
});
This is a very simplistic example of that sets a variable on the $rootScope, but you could instead run a function (helpful if you want to save form values or prompt the user before changing your route for example) or inject a service (such as if you want to check if a user is logged in), etc.

Is there a way to stop all other events in a dojo onBlur event handler?

Maybe I'm on the wrong track...
The setup:
I have a rather complex full dojofied web application. The important part for this question is a longish form in the central region of a dijit.layout.BorderContainer with a navigation tree and some action buttons in the other regions.
What I want to do:
If the user did enter data and did not save, they should get a warning message if he is going to leave the form (if he navigates away, klicks the "new Element" button,...). For a better user experience, I wanted to give a modal dialog with the options "save", "leave anyway", "cancel".
May idea was to use the onBlur event of the form, stop all other events (most likely an onClick on some other widget), check for changes, if there are changes, display the dialog, otherwise let the other events continue.
I do not want to add a checkChanges method to all non-form active elements!
For a first test I just tried to stop the events...
This works
<div id="formArea" dojoType="dijit.form.Form" encoding="multipart/form-data" action="" class="ContentPane" region="center">
<script type="dojo/connect" event="onBlur" >
alert("I don't think so");
</script>
</div>
...but it's ugly and I can't easily continue
This doesn't
<div id="formArea" dojoType="dijit.form.Form" encoding="multipart/form-data" action="" class="ContentPane" region="center">
<script type="dojo/connect" event="onBlur" args="e">
console.log("blur"); // ok
e.preventDefault();//event.stopt(e)//return false //<--neither of these
</script>
</div>
the problem is that if I click on a button outside of the form, the onBlur triggers, but I can't stop the onClick on the button.
I know that onBlur doesn't deliver an event object - so the e.something can't really work...
Is there any way to catch the onClick on the other element?
Pause button event listener(s) in form's onBlur if data are not saved.
See it in action: http://jsfiddle.net/phusick/A5DHf/
You have some button event listeners, register them via on.pausable(node, event, callback) instead of on():
var b1Handler = on.pausable(button1Node, "click", function() {
console.log("b1.onClick");
});
var b2Handler = on.pausable(button2Node, "click", function() {
console.log("b2.onClick");
});
Gather handlers into an array:
var handlersToPause = [b1Handler, b2Handler];
Add onBlur event listener:
on(form, "blur", function(e) {
if (this.isDirty()) {
// if data are not saved, pause button event listeners
handlersToPause.forEach(function(handler) {
handler.pause();
});
// display modal dialog here
}
});
Add e.g. onFocus event listener to resume button event listeners:
on(form, "focus", function(e) {
handlersToPause.forEach(function(handler) {
handler.resume();
});
});
Please note, that handler.pause() is pausing an onclick listener, not an event. The onclick event is waiting in the Event queue and therefore is not accessible in the execution time of onblur.
I would work out some more robust solution, but this is quick and answers your question. Anyway, have a look at dojo/aspect and its around advice to call your checkChanges without the need to change all non-form active elements.
there is afaik only confirm('question?') that will 'deadlock' the events of your page like that.
I have made a similar setup though, the way I came around this (except if user enters url in addressbar and hits enter) was a popup dialog whenever the navigation tree is clicked, sending user to a new view. Consider:
----------------------------------------
| Nav 1 | Asset1 ( view controller ) |
| Nav 2 | Asset2 ( hidden ) |
----------------------------------------
Nav 1 is the default onload view, Asset 1 is loaded, contains a 'setup page' form or similar and can be changed. The trick is, Asset1 and Asset2 is derivative from AbstractAsset which in turn is a simple ContentPane extension.
In AbstractAsset is a 'must-override-for-functionality' function called isDirty
var Viewcontroller = declare("AbstractAsset", [dijit.ContentPane], {
isDirty: function() { return false; }
});
declare("Asset1", [Viewcontroller], {
startup: function() {
// sets up form
...
// and references inputfields to 'this'
this.inputfields = this.form.getChildren();
// and saves (clones) the state of each field
var self = this;
this.inputfields.forEach(function(inputWidget) {
self.states[inputWidget.id] = inputWidget.get("value");
});
},
isDirty: function() {
var self = this;
var dirty = false;
this.form.getChildren().some(input) {
if(self.states[input.id] != input.get("value")) {
dirty = true;
return false; // breaks .some loop
}
return true;
});
return dirty;
}
})
Then in turn, every navigation click must call the currently visible view controller's isDirty function in order to procede. Lets say user clicks the nav-tree (dijit.Tree) row node Nav 2.
var navigation = dojo.declare("NavigationController", [dijit.Tree], {
currentView : null,
onLoad: function() {
// start Asset1 in viewNode by default
this.currentView = new Asset1({ }, this.viewNode);
},
onClick : function() {
if(this.currentView.isDirty()) alert("I Dont Think So");
else {
this.loadFunction(this.model.selection.getSelected());
}
}
});
This is the general idea of implementing the on-unload-check, you Will need to hook any onClick events through your 'master application controller' to determine what should happen. Check this application which serves as cms navigation controller and its page.js:587 for isDirty example

Categories

Resources