JavaScript Module pattern and Drag and Drop API - javascript

I have a sample module in js that is suppose to manage Drag and Drop file upload.
The code seems to work for 'dragenter' event function but when I drop the file, and 'drop' event should call the dropped function, the code always forwards to the file path.
Here is the code sample
var testModule = (function testBuilder(){
function call(evt) {
evt.preventDefault();
console.log('works');
}
function dropped(evt) {
evt.preventDefault();
console.log('file dropped');
}
var element = document.getElementById('testBlock');
function init() {
element.addEventListener('dragenter', call, false);
element.addEventListener('drop', dropped, false);
}
publicAPI = {
init: init
};
return publicAPI;
})();
window.onload = function() {
testModule.init();
};
and a jsbin here https://jsbin.com/redixucate/edit?js,console,output
If anyone can figure out why it keeps redirecting the file path, I would rly appreciate it.

Add 'dragover' event with preventDefault and it should work.
Inside your init():
element.addEventListener('dragover', over, false);
and over function:
function over(e) {
e = e || window.event;
if(e.preventDefault) {
e.preventDefault();
}
}
Also add the same prevention to your other two functions ..
See https://jsbin.com/xemovariwu/1/edit?js,console,output
Also see this question/answer.

Related

Multiple window onload functions with only Javascript

I have an external JS file that adds a window.onload function to the page.
The basic premise is that it loads up a popup window on your website whenever the user clicks on certain link class. It's written in PHP / JS so assume that the function works by itself.
Inside this JS file has the following code.
window.onload = function() {
var anchors = document.getElementsByClassName("vyper-triggers");
for (var i = 0; i < anchors.length; i++) {
var anchor = anchors[i];
anchor.onclick = function() {
if (isMobile.any()) {
window.open("$url");
} else {
document.getElementById("clickonthis").click();
}
}
}
}
Now my problem is when my user wants to add 2 different popup windows, the window.onload function doesn't stack. Also because this is an embedded javascript that my user adds himself, there is no way for me to put both functions inside one big window.onload function.
My user might put one JS file in one area of their site, and another JS file in another area, if that makes sense.
So how do I make it so that the window.onload function will stack no matter the placing of these external JS files on the page and considering that each function must be kept separate?
Rather than setting window.onload, you should use addEventListener. Listeners added this way will stack automatically.
window.addEventListener('load', function() {
console.log('First listener');
});
window.addEventListener('load', function() {
console.log('Second listener');
});
window.addEventListener('load', function() {
console.log('Third listener');
});
If you have to support versions of IE before IE9, there's a polyfill which will make this work correctly.
Probably you have multiple files and u want to check something onload.
Let's implement a basic function to add other functions and run all of them when the event onload is triggered.
So, first we check if windows.onload has a function object if not add our function. If is contains a function object merge it with our function like this:
function addLoadEvent(callback) {
const previous = window.onload
if (typeof previous === 'function') {
window.onload = (e) => {
if (previous) previous(e)
callback(e)
}
}
...
}
This is an example how to use it:
function addLoadEvent(callback) {
const previous = window.onload
if (typeof previous === 'function') {
window.onload = (e) => {
if (previous) previous(e)
callback(e)
}
} else {
window.onload = callback
}
}
function func1() {
console.log('This is the first.')
}
function func2() {
console.log('This is the second.')
}
addLoadEvent(func1);
addLoadEvent(func2);
addLoadEvent(() => {
console.log('This is the third.')
document.body.style.backgroundColor = '#EFDF95'
})

How to register a cordova event inside the angular controller within an hybrid application

I'm using Visual Studio>TypeScript>Cordova application to build foundation of my application. then i install angularJs 1.3 at it was required by mobile-angular-ui, and then install it.
The application has index.ts, which later it's compailation result will be placed inside appBundle.js ....
The file is like this and work just fine:
// For an introduction to the Blank template, see the following documentation:
// http://go.microsoft.com/fwlink/?LinkID=397705
// To debug code on page load in Ripple or on Android devices/emulators: launch your app, set breakpoints,
// and then run "window.location.reload()" in the JavaScript Console.
var RavisMobileCordova;
(function (RavisMobileCordova) {
"use strict";
var Application;
(function (Application) {
function initialize() {
document.addEventListener('deviceready', onDeviceReady, false);
}
Application.initialize = initialize;
function onDeviceReady() {
// Handle the Cordova pause and resume events
document.addEventListener('pause', onPause, false);
document.addEventListener('resume', onResume, false);
// TODO: Cordova has been loaded. Perform any initialization that requires Cordova here.
}
function onPause() {
// TODO: This application has been suspended. Save application state here.
}
function onResume() {
// TODO: This application has been reactivated. Restore application state here.
}
})(Application = RavisMobileCordova.Application || (RavisMobileCordova.Application = {}));
window.onload = function () {
Application.initialize();
};
})(RavisMobileCordova || (RavisMobileCordova = {}));
//# sourceMappingURL=appBundle.js.map
but when it's come to the stage, that i may be in need of calling an events inside my controller, i can't do that, the debugger detach and doesn't show any help full error.
i tried it with use of onResume for test propose, first by seeing this:
https://groups.google.com/forum/#!topic/angular/nx3Csp8nu0A
and replacing the line :
document.addEventListener('resume', onResume, false);
with the following, and defining $scope.onResume=function... in my controller:
document.addEventListener('resume', $scope.onResume, false);
Failed!!!
Then i done document.addEventListener(...) within my own controller
Failed!!!
then i look closer at appBundle.js and saw if register on resume just when device ready is called, so i did this within the body of controller:
//Only Device Ready Here
document.addEventListener('deviceready', $scope.DeviceEvents.onDeviceReady, false);
$scope.DeviceEvents={
onDeviceReady: function(){
//Register Device Events Here
document.addEventListener('resume', $scope.DeviceEvents.onResume, false);
},
onResume:function(){
alert("resuming!!!");
}
};
And again,
Failed!!!
Now, does any one knows how should i do it side by mobile-angular-ui? how to register any kinds of event inside the app, which come from device.
If i'm wrong any where, Sorry, I'm new to all of these stuff, Android, Cordova, and know the basic of Angular, ... someone who's boss told him to write an application, that i'm not familiar with any part of it.
Thank you,
Hassan F.
Edit
I also find this just now, it been used withing the angular it self, but it still doesn't have any thing to do with the controller...
MyModule.run(function ($rootScope, $http, dataService, $window, $q, $location, localize) {
document.addEventListener("deviceready", onDeviceReady, false);
function onDeviceReady() {
//Initialize anything you need to. aka: Google analytics.
//Set other evens you need to listen to.
document.addEventListener("online", onOnline, false);
document.addEventListener("offline", onOffline, false);
}
}
here's what i did... First, i remove the ng-app, from my HTML
then i register onDeviceReady event, and inside that, then i append ng-app to my html, so after device become ready, angular will start it's processes.
Second, i have multi-layered structure, which mean, i have a master page, which sit on HTML tag, and other controller are within this one, using ng-view and route config.
so i bind events to my master controller:
like this:
module RavisMobileCordova {
export module Application {
export function initialize() {
var matchesDevice = navigator.userAgent.match(/(iPhone|iPod|iPad|Android|BlackBerry|IEMobile)/);
isBrowser = !(matchesDevice!=null && matchesDevice.length > 0);
document.addEventListener("deviceready", onDeviceReady, false);
if (isBrowser) {
// Create the event
var event = new CustomEvent("deviceready", {});
// Dispatch/Trigger/Fire the event
document.dispatchEvent(event);
}
}
function onDeviceReady(evt) {
angular.element(document).ready(() => {
var domElement = document.getElementById("appElement");
angular.bootstrap(domElement, ["ravisMobileApp"]);
});
// Handle the Cordova pause and resume events
//TRUE: Capture Phase, FALSE: Bubbling Phase
document.addEventListener("pause", onPause, false);
document.addEventListener("resume", onResume, false);
document.addEventListener("backbutton", onBackButton, true);
document.addEventListener("offline", onOffline, false);
document.addEventListener("online", onOnline, false);
// TODO: Cordova has been loaded. Perform any initialization that requires Cordova here.
var scope:any = angular.element("[ng-controller='MasterController'], [data-ng-controller='MasterController']").scope();
scope.SystemApi.Navigator = navigator;
scope.SystemEvents.OnStart(evt);
}
function onPause() {
// TODO: This application has been suspended. Save application state here.
}
function onResume() {
// TODO: This application has been reactivated. Restore application state here.
}
function onBackButton(evt) {
//evt.preventDefault();
//evt.stopPropagation();
var scope:any = angular.element("[ng-controller='MasterController'], [data-ng-controller='MasterController']").scope();
scope.SystemEvents.BackButton(evt);
}
function onOffline() {
var scope: any = angular.element("[ng-controller='MasterController'], [data-ng-controller='MasterController']").scope();
scope.SystemEvents.OnOffline();
}
function onOnline() {
var scope: any = angular.element("[ng-controller='MasterController'], [data-ng-controller='MasterController']").scope();
scope.SystemEvents.OnOnline();
}
}
window.onload = () => {
Application.initialize();
}
}
then i define this section within my master controller, which get called on registerd events...
$scope.SystemEvents = {
OnStart: function () {
LocalStorageService.NavigatorHistory.clearHistory();
angular.element("#master_overlay").removeAttr("style");
if ($scope.SystemMethods.IsNetworkOffline()) {
//$scope.SystemEvents.OnOffline();
// Create the event
var event = new CustomEvent("offline", {});
// Dispatch/Trigger/Fire the event
document.dispatchEvent(event);
}
},
OnOnline: function () {
if (!Utility.isNullOrUndefined(eventList[EventTypes.Online])) {
for (var i = 0; i < eventList[EventTypes.Online].length; i++) {
eventList[EventTypes.Online][i].callBack();
}
}
$scope.SystemMethods.HideMasterOverlay();
},
OnOffline: function() {
if (!Utility.isNullOrUndefined(eventList[EventTypes.Offline])) {
for (var i = 0; i < eventList[EventTypes.Offline].length; i++) {
eventList[EventTypes.Offline][i].callBack();
}
}
$scope.SystemMethods.ShowMasterOverlay();
},
BackButton: function (evt) {
//Bind Cancel
evt.cancel = false;
//Call All Registered Events
if (!Utility.isNullOrUndefined(eventList[EventTypes.BackButton])) {
for (var i = 0; i < eventList[EventTypes.BackButton].length; i++) {
eventList[EventTypes.BackButton][i].callBack(evt);
}
}
//If Cancel Do Return
if (evt.cancel)
return;
//TODO: API Cancelable Code Here
$scope.SystemMethods.MoveToPreviousPage();
//evt.preventDefault();
//evt.stopPropagation();
},
HistoryBack: $scope.SystemMethods.MoveToPreviousPage
};
now if i ever need these within my master page, i do what i need in here, else, if i need them in my other controllers...
here's what i gonna do:
Master Controller:
var eventList = {};
$scope.SystemMethods = {
RegisterEvent: function(eventType, callBack) {
if (Utility.isNullOrUndefined(eventList[eventType]))
eventList[eventType] = [];
eventList[eventType].push({ eventType: eventType, callBack: callBack });
},
UnregisterEvent: function(eventType, callBack) {
var delList = Enumerable.From(eventList[eventType]).Where(function (w) {
return w.eventType === eventType && w.callBack === callBack;
}).ToArray();
for (var i = 0; i < delList.length; i++) {
var objIndex = Enumerable.From(eventList[eventType]).IndexOf(delList[i]);
eventList[eventType].splice(objIndex, 1);
}
}
}
Other controllers:
You also have to unregister what you register, since your master controller is persist and you pass your function to the variable within that controller, they won't go away by their own selves
$scope.initialize = function () {
$scope.SystemMethods.RegisterEvent(EventTypes.BackButton, $scope.Events.backButton);
$scope.SystemMethods.RegisterEvent(EventTypes.Online, $scope.Events.online);
}
$scope.$on("$destroy", function () {
$scope.SystemMethods.UnregisterEvent(EventTypes.Online, $scope.Events.online);
$scope.SystemMethods.UnregisterEvent(EventTypes.BackButton, $scope.Events.backButton);
});

Catching all audio end events from dynamic content in jquery

I have seen similar questions - but not that fix my problem!
I have audio on my page and when one ends, I want the next to start, but I can't even get the ended to trigger...
I cut the code down to this:
function DaisyChainAudio() {
$().on('ended', 'audio','' ,function () {
alert('done');
});
}
This is called from my page/code (and is executed, setting a break point shows that).
As far as I understand this should set the handler at the document level, so any 'ended' events from any 'audio' tag (even if added dynamically) should be trapped and show me that alert...
But it never fires.
edit
With some borrowing from Çağatay Gürtürk's suggestion so far have this...
function DaisyChainAudio() {
$(function () {
$('audio').on('ended', function (e) {
$(e.target).load();
var next = $(e.target).nextAll('audio');
if (!next.length) next = $(e.target).parent().nextAll().find('audio');
if (!next.length) next = $(e.target).parent().parent().nextAll().find('audio');
if (next.length) $(next[0]).trigger('play');
});
});
}
I'd still like to set this at the document level so I don't need to worry about adding it when dynamic elements are added...
The reason it does not fire is, media events( those specifically belonging to audio or video like play, pause, timeupdate, etc) do not get bubbled. you can find the explanation for that in the answer to this question.
So using their solution, I captured the ended event, and this would allow setting triggers for dynamically added audio elements.
$.createEventCapturing(['ended']); // add all the triggers for which you like to catch.
$('body').on('ended', 'audio', onEnded); // now this would work.
JSFiddle demo
the code for event capturing( taken from the other SO answer):
$.createEventCapturing = (function () {
var special = $.event.special;
return function (names) {
if (!document.addEventListener) {
return;
}
if (typeof names == 'string') {
names = [names];
}
$.each(names, function (i, name) {
var handler = function (e) {
e = $.event.fix(e);
return $.event.dispatch.call(this, e);
};
special[name] = special[name] || {};
if (special[name].setup || special[name].teardown) {
return;
}
$.extend(special[name], {
setup: function () {
this.addEventListener(name, handler, true);
},
teardown: function () {
this.removeEventListener(name, handler, true);
}
});
});
};
})();
Try this:
$('audio').on('ended', function (e) {
alert('done');
var endedTag=e.target; //this gives the ended audio, so you can find the next one and play it.
});
Note that when you create a new audio dynamically, you should assign the events. A quick and dirty solution would be:
function bindEvents(){
$('audio').off('ended').on('ended', function (e) {
alert('done');
var endedTag=e.target; //this gives the ended audio, so you can find the next one and play it.
});
}
and run bindEvents whenever you create/delete an audio element.

Detecting a form.submit() performed via JavaScript

In my page there is a frame that belongs to the same domain. The content of this frame is varied and relatively unpredictable. Whenever a user clicks a button (inside the frame) that performs a post, I need to execute a function that performs some UI tasks. The problem is that I cannot edit the source of these frames for reasons beyond my control. Some of these buttons are simple form submit buttons, but others do not directly submit the form, but instead have an onclick handler that performs some checks and might submit.
Here is the problem: How do I detect if one of these onclick handlers called form.submit()? If there's no handler, then obviously I can set up a handler for onsubmit(), but is not the case for all of these buttons.
This is my code so far:
function addEventBefore(element, type, before, after) {
var old = element['on' + type] || function() {};
before = before || function() {};
after = after || function() {};
element['on' + type] = function () {
before();
old();//I can't modify this old onclick handler
after();
};
}
function setup() {
console.log('setup');
}
function takedown() {
// In this method, I want to know if old() caused a form submit
console.log('takedown');
}
function $includeFrames(jQuery, selector) {
return jQuery(selector).add(jQuery('iframe').contents().find(selector));
}
var a = $includeFrames($, 'input[type="submit"], input[type="button"]').each(function() {
var elem = $(this)[0];
addEventBefore(elem, 'click', setup, takedown);
});
In the onload event of the iframe you'll need to hook up an event listener to each form in the iframed page. You need to do this on every load, as each fresh page needs new listeners.
$("#someIframe").on('load',function() {
$(this).contents().find("form").each(function() {
$(this).on('submit',function() {... your code...})
})
}
The solution that worked for me came from a friend of mine. The solution is to shim the form.submit() function.
$(function() {
var el = document.getElementById('myform');
el.submit = function(fn) {
return function() {
myFunctionGoesHere();
fn.apply(this, arguments);
};
}(el.submit);
});
Here is a working example: http://jsfiddle.net/hW6Z4/9/

Unobtrusive javascript not working with AJAX loaded content

I am facing very strange thing with AJAX and Unobtrusive JavaScript that I have two pages
ajaxcontent.php
index.php
index.php has
<div id="cont"></div>
<input type="button" id="a" value="load plz.">
<script type="text/javascript">
document.getElementById('a').onclick = function () {
if (window.XMLHttpRequest) {
ari = new XMLHttpRequest();
} else {
ari = new ActiveXObject("Microsoft.XMLHTTP")
}
ari.onreadystatechange = function () {
if (ari.readyState == 4 && ari.status == 200) {
document.getElementById('cont').innerHTML = ari.responseText;
}
}
ari.open("GET","button.php",true);
ari.send();
}
document.getElementById('b').onclick = function () {
alert('a');
}
</script>
And ajaxcontent.php has only
<input type="button" id="b"/>
and the problem is unobtrusive Javascript is not working.
After laoding of ajaxcontent when i click on button it doesn't show alert pop up.
i have tried that i added
document.getElementById('b').onclick = function () {
alert('a');
}
this code on ajaxcontent.php but it still not working.
THe only way to make it work that i have to add inline javascript as
<input type="button" id="b" onclick="hi();"/>
and replace this function with
document.getElementById('b').onclick = function () {
alert('a');
}
with
function hi() {
alert('a');
}
so please help me that how to use unobtrusive js here and please don't give jQuery based answer thanks
First of all document.getElementById('b') can only find an element that is in the DOM at the time you call this function.
Because the element with the id b is in the data you request in the click event, the function will not find any element. You most likely should have seen an error in the console like cannot set property onclick of undefined.
AJAX requests are async by default (and you should not make them sync because this will block the window of the browser).
So you need to place the document.getElementById('b').onclick = ... in the onreadystatechange check right after the document.getElementById('cont').innerHTML = ari.responseText;
Here a simple example how to generalize your request:
function doAjaxRequest(url, complete, error) {
var ari; //<<<< you should define your variables using var otherwise it is set in the global scope
if (window.XMLHttpRequest) {
ari = new XMLHttpRequest();
} else {
ari = new ActiveXObject('Microsoft.XMLHTTP');
}
ari.onreadystatechange = function() {
if (ari.readyState === 4) {
if (ari.status === 200) {
// if complete callback is passed, then call it if request was successful
if (typeof complete === 'function') {
complete(ari.responseText);
}
} else {
// if error callback is passed then call it if request was not successful
if (typeof error === 'function') {
error(ari.status, ari.statusText);
}
}
}
}
ari.open('GET', url, true);
ari.send(null);
}
document.getElementById('a').onclick = function() {
doAjaxRequest('button.php', function( data ) {
document.getElementById('cont').innerHTML = data;
document.getElementById('b').onclick = function() {
alert('a');
}
}, function(errorCode, errorMessage) {
//do something on error
});
}
The onclick event attaches to elements currently in the DOM when the function is triggered. Because the button in ajaxcontent.php is added to the DOM after the function was called, no event is attached.
To rectify this, you can add a snippet inside ari.onreadystatechange to detatch events then attach the event again.
ari.onreadystatechange = function () {
if (ari.readyState == 4 && ari.status == 200) {
document.getElementById('cont').innerHTML = ari.responseText;
// remove events
document.getElementById('b').onclick = null;
// attach events
document.getElementById('b').onclick = function() {
alert('a');
}
}
}
It's important to remove events because it may (although i was using jQuery when i learnt by mistake) cause double execution.
If you add javascript code inside ajaxcontent.php, that code will not be executed unless you extend your ari.onreadystatechange function to scan for javascript and execute it. The way I do this, is I put my javascript in AJAX requested pages in a input with class 'ajax-js' and scan for those input boxes and execute the code one by one, removing the class as I go.

Categories

Resources