Click outside to hide container with button - javascript

I am using the following Angular directive to hide div elements when the user clicks or touches outside.
https://github.com/TheSharpieOne/angular-off-click
It is working as expected but when you click outside of a div on a button that toggles the div the 'angular-off-click' callback is fired to hide the container but then the toggle function attached to the button is called, reopening the div.
The 'off-click-filter' solves this by adding exceptions which use css selectors to check before the hide function is called. However this was removed as we did not want the extra bloat of css class exceptions in the html markup.
The desired function is for the toggle button not to fire its handler when you click outside of the container
Update
It is only a problem on touch devices where there is a 300ms delay by default. So this means that the callback is fired to hide the container then the toggle functions runs after 300ms, reopening the container. On desktop, with a mouse click, the toggle function fires first and then the callback
// Angular App Code
var app = angular.module('myApp', ['offClick']);
app.controller('myAppController', ['$scope', '$timeout', function($scope,$timeout) {
$scope.showContainer = false;
$scope.toggleContainer = function() {
$timeout(function() {
$scope.showContainer = !$scope.showContainer;
}, 300);
};
$scope.hideContainer = function(scope, event, p) {
$scope.showContainer = false;
console.log('event: ', event);
console.log('scope: ', scope);
console.log(p);
};
}]);
// Off Click Directive Code
angular.module('offClick', [])
.directive('offClick', ['$rootScope', '$parse', function ($rootScope, $parse) {
var id = 0;
var listeners = {};
// add variable to detect touch users moving..
var touchMove = false;
// Add event listeners to handle various events. Destop will ignore touch events
document.addEventListener("touchmove", offClickEventHandler, true);
document.addEventListener("touchend", offClickEventHandler, true);
document.addEventListener('click', offClickEventHandler, true);
function targetInFilter(target, elms) {
if (!target || !elms) return false;
var elmsLen = elms.length;
for (var i = 0; i < elmsLen; ++i) {
var currentElem = elms[i];
var containsTarget = false;
try {
containsTarget = currentElem.contains(target);
} catch (e) {
// If the node is not an Element (e.g., an SVGElement) node.contains() throws Exception in IE,
// see https://connect.microsoft.com/IE/feedback/details/780874/node-contains-is-incorrect
// In this case we use compareDocumentPosition() instead.
if (typeof currentElem.compareDocumentPosition !== 'undefined') {
containsTarget = currentElem === target || Boolean(currentElem.compareDocumentPosition(target) & 16);
}
}
if (containsTarget) {
return true;
}
}
return false;
}
function offClickEventHandler(event) {
// If event is a touchmove adjust touchMove state
if( event.type === 'touchmove' ){
touchMove = true;
// And end function
return false;
}
// This will always fire on the touchend after the touchmove runs...
if( touchMove ){
// Reset touchmove to false
touchMove = false;
// And end function
return false;
}
var target = event.target || event.srcElement;
angular.forEach(listeners, function (listener, i) {
if (!(listener.elm.contains(target) || targetInFilter(target, listener.offClickFilter))) {
$rootScope.$evalAsync(function () {
listener.cb(listener.scope, {
$event: event
});
});
}
});
}
return {
restrict: 'A',
compile: function ($element, attr) {
var fn = $parse(attr.offClick);
return function (scope, element) {
var elmId = id++;
var offClickFilter;
var removeWatcher;
offClickFilter = document.querySelectorAll(scope.$eval(attr.offClickFilter));
if (attr.offClickIf) {
removeWatcher = $rootScope.$watch(function () {
return $parse(attr.offClickIf)(scope);
}, function (newVal) {
if (newVal) {
on();
} else if (!newVal) {
off();
}
});
} else {
on();
}
attr.$observe('offClickFilter', function (value) {
offClickFilter = document.querySelectorAll(scope.$eval(value));
});
scope.$on('$destroy', function () {
off();
if (removeWatcher) {
removeWatcher();
}
element = null;
});
function on() {
listeners[elmId] = {
elm: element[0],
cb: fn,
scope: scope,
offClickFilter: offClickFilter
};
}
function off() {
listeners[elmId] = null;
delete listeners[elmId];
}
};
}
};
}]);
/* Styles go here */
.container {
background: blue;
color: #fff;
height: 300px;
width: 300px;
}
<!DOCTYPE html>
<html>
<head>
<script src="https://code.angularjs.org/1.4.0/angular.js"></script>
<link rel="stylesheet" href="style.css" />
</head>
<body data-ng-app="myApp">
<h1>Hello Plunker!</h1>
<div data-ng-controller="myAppController">
<button data-ng-click="toggleContainer()">Toggle Container</button>
<div class="container" data-ng-show="showContainer" data-off-click="hideContainer()" data-off-click-if="showContainer">
This is the container
</div>
</div>
</body>
</html>
http://jsbin.com/hibovu

The problem is that when you click on the button the both of the functions are firing:
hideContainer from the directive.
toggleContainer from the click event (that showing the div again).
The solution
Add event.stopPropagation(); before you evaluate the hide callback.
How do you do this?
Pass the event to the function data-off-click="hideContainer($event)".
Add the $event param in the definition of the hideContainer function in the $scope like this: $scope.hideContainer = function($event)
And the full code:
// Angular App Code
var app = angular.module('myApp', ['offClick']);
app.controller('myAppController', ['$scope', '$timeout', function($scope,$timeout) {
$scope.showContainer = false;
$scope.toggleContainer = function() {
$timeout(function() {
$scope.showContainer = !$scope.showContainer;
}, 300);
};
$scope.hideContainer = function($event) {
$event.stopPropagation();
$timeout(function(){
$scope.showContainer = false;
});
};
}]);
// Off Click Directive Code
angular.module('offClick', [])
.directive('offClick', ['$rootScope', '$parse', function ($rootScope, $parse) {
var id = 0;
var listeners = {};
// add variable to detect touch users moving..
var touchMove = false;
// Add event listeners to handle various events. Destop will ignore touch events
document.addEventListener("touchmove", offClickEventHandler, true);
document.addEventListener("touchend", offClickEventHandler, true);
document.addEventListener('click', offClickEventHandler, true);
function targetInFilter(target, elms) {
if (!target || !elms) return false;
var elmsLen = elms.length;
for (var i = 0; i < elmsLen; ++i) {
var currentElem = elms[i];
var containsTarget = false;
try {
containsTarget = currentElem.contains(target);
} catch (e) {
// If the node is not an Element (e.g., an SVGElement) node.contains() throws Exception in IE,
// see https://connect.microsoft.com/IE/feedback/details/780874/node-contains-is-incorrect
// In this case we use compareDocumentPosition() instead.
if (typeof currentElem.compareDocumentPosition !== 'undefined') {
containsTarget = currentElem === target || Boolean(currentElem.compareDocumentPosition(target) & 16);
}
}
if (containsTarget) {
return true;
}
}
return false;
}
function offClickEventHandler(event) {
// If event is a touchmove adjust touchMove state
if( event.type === 'touchmove' ){
touchMove = true;
// And end function
return false;
}
// This will always fire on the touchend after the touchmove runs...
if( touchMove ){
// Reset touchmove to false
touchMove = false;
// And end function
return false;
}
var target = event.target || event.srcElement;
angular.forEach(listeners, function (listener, i) {
if (!(listener.elm.contains(target) || targetInFilter(target, listener.offClickFilter))) {
//$rootScope.$evalAsync(function () {
listener.cb(listener.scope, {
$event: event
});
//});
}
});
}
return {
restrict: 'A',
compile: function ($element, attr) {
var fn = $parse(attr.offClick);
return function (scope, element) {
var elmId = id++;
var offClickFilter;
var removeWatcher;
offClickFilter = document.querySelectorAll(scope.$eval(attr.offClickFilter));
if (attr.offClickIf) {
removeWatcher = $rootScope.$watch(function () {
return $parse(attr.offClickIf)(scope);
}, function (newVal) {
if (newVal) {
on();
} else if (!newVal) {
off();
}
});
} else {
on();
}
attr.$observe('offClickFilter', function (value) {
offClickFilter = document.querySelectorAll(scope.$eval(value));
});
scope.$on('$destroy', function () {
off();
if (removeWatcher) {
removeWatcher();
}
element = null;
});
function on() {
listeners[elmId] = {
elm: element[0],
cb: fn,
scope: scope,
offClickFilter: offClickFilter
};
}
function off() {
listeners[elmId] = null;
delete listeners[elmId];
}
};
}
};
}]);
.container {
background: blue;
color: #fff;
height: 300px;
width: 300px;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body data-ng-app="myApp">
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.0/angular.min.js"></script>
<h1>Hello Plunker!</h1>
<div data-ng-controller="myAppController">
<button data-ng-click="toggleContainer()">Toggle Container</button>
<div class="container" data-ng-show="showContainer" data-off-click="hideContainer($event)" data-off-click-if="showContainer">
This is the container
</div>
</div>
</body>
</html>
http://jsbin.com/hibovu/3/edit?html,css,js

Related

Appending DOM Loses Menu Drop Down Functionality

I am appending dynamic DOM elements (after Ajax calls to gather the data) as seen below. I have narrowed the code down to what I believe is most important:
<!-- SideMenu HTML -->
<div id="sidebar-menu">
<ul id="folders">
<!-- AJAX DATA POPULATES MENU HERE -->
<!-- HTML File calling JS function -->
<script>
$(document).ready(function() {
getParentFolders('parentFolderTitle');
});
</script>
// JS File function...
var parentFolders; // Values retrieved from AJAX calls
function buildFolderMenu(index) {
var markup = '<li class="has_sub">' +
'<a ... > parentFolders[index].Name + '</a>' +
'<ul ...>' +
'<li> SUB FOLDER TEST <li>' +
'</ul>' +
'</li>';
$(#folders').append(markup);
}
EDIT: My code is interacting with a 3rd party template, after reading the replies, I was able to track down the file which handles the 'click events'. However, based on my own code shown above, I am not sure how to adjust the template code to work with my dynamically appended DOM.
Here is the template code handling the click: (I can see the 'menuItemClick' function is where this is likely handled, but how do I apply the '.on('', function())' adjustments here, based on how this file is written?)
/**
* Theme: Adminto Admin Template
* Author: Coderthemes
* Module/App: Main Js
*/
!function($) {
"use strict";
var Sidemenu = function() {
this.$body = $("body"),
this.$openLeftBtn = $(".open-left"),
this.$menuItem = $("#sidebar-menu a")
};
Sidemenu.prototype.openLeftBar = function() {
$("#wrapper").toggleClass("enlarged");
$("#wrapper").addClass("forced");
if($("#wrapper").hasClass("enlarged") && $("body").hasClass("fixed-left")) {
$("body").removeClass("fixed-left").addClass("fixed-left-void");
} else if(!$("#wrapper").hasClass("enlarged") && $("body").hasClass("fixed-left-void")) {
$("body").removeClass("fixed-left-void").addClass("fixed-left");
}
if($("#wrapper").hasClass("enlarged")) {
$(".left ul").removeAttr("style");
} else {
$(".subdrop").siblings("ul:first").show();
}
toggle_slimscroll(".slimscrollleft");
$("body").trigger("resize");
},
//menu item click
Sidemenu.prototype.menuItemClick = function(e) {
if(!$("#wrapper").hasClass("enlarged")){
if($(this).parent().hasClass("has_sub")) {
}
if(!$(this).hasClass("subdrop")) {
// hide any open menus and remove all other classes
$("ul",$(this).parents("ul:first")).slideUp(350);
$("a",$(this).parents("ul:first")).removeClass("subdrop");
$("#sidebar-menu .pull-right i").removeClass("md-remove").addClass("md-add");
// open our new menu and add the open class
$(this).next("ul").slideDown(350);
$(this).addClass("subdrop");
$(".pull-right i",$(this).parents(".has_sub:last")).removeClass("md-add").addClass("md-remove");
$(".pull-right i",$(this).siblings("ul")).removeClass("md-remove").addClass("md-add");
}else if($(this).hasClass("subdrop")) {
$(this).removeClass("subdrop");
$(this).next("ul").slideUp(350);
$(".pull-right i",$(this).parent()).removeClass("md-remove").addClass("md-add");
}
}
},
//init sidemenu
Sidemenu.prototype.init = function() {
var $this = this;
var ua = navigator.userAgent,
event = (ua.match(/iP/i)) ? "touchstart" : "click";
//bind on click
this.$openLeftBtn.on(event, function(e) {
e.stopPropagation();
$this.openLeftBar();
});
// LEFT SIDE MAIN NAVIGATION
$this.$menuItem.on(event, $this.menuItemClick);
// NAVIGATION HIGHLIGHT & OPEN PARENT
$("#sidebar-menu ul li.has_sub a.active").parents("li:last").children("a:first").addClass("active").trigger("click");
},
//init Sidemenu
$.Sidemenu = new Sidemenu, $.Sidemenu.Constructor = Sidemenu
}(window.jQuery),
function($) {
"use strict";
var FullScreen = function() {
this.$body = $("body"),
this.$fullscreenBtn = $("#btn-fullscreen")
};
//turn on full screen
// Thanks to http://davidwalsh.name/fullscreen
FullScreen.prototype.launchFullscreen = function(element) {
if(element.requestFullscreen) {
element.requestFullscreen();
} else if(element.mozRequestFullScreen) {
element.mozRequestFullScreen();
} else if(element.webkitRequestFullscreen) {
element.webkitRequestFullscreen();
} else if(element.msRequestFullscreen) {
element.msRequestFullscreen();
}
},
FullScreen.prototype.exitFullscreen = function() {
if(document.exitFullscreen) {
document.exitFullscreen();
} else if(document.mozCancelFullScreen) {
document.mozCancelFullScreen();
} else if(document.webkitExitFullscreen) {
document.webkitExitFullscreen();
}
},
//toggle screen
FullScreen.prototype.toggle_fullscreen = function() {
var $this = this;
var fullscreenEnabled = document.fullscreenEnabled || document.mozFullScreenEnabled || document.webkitFullscreenEnabled;
if(fullscreenEnabled) {
if(!document.fullscreenElement && !document.mozFullScreenElement && !document.webkitFullscreenElement && !document.msFullscreenElement) {
$this.launchFullscreen(document.documentElement);
} else{
$this.exitFullscreen();
}
}
},
//init sidemenu
FullScreen.prototype.init = function() {
var $this = this;
//bind
$this.$fullscreenBtn.on('click', function() {
$this.toggle_fullscreen();
});
},
//init FullScreen
$.FullScreen = new FullScreen, $.FullScreen.Constructor = FullScreen
}(window.jQuery),
//main app module
function($) {
"use strict";
var App = function() {
this.VERSION = "1.5.0",
this.AUTHOR = "Coderthemes",
this.SUPPORT = "coderthemes#gmail.com",
this.pageScrollElement = "html, body",
this.$body = $("body")
};
//on doc load
App.prototype.onDocReady = function(e) {
FastClick.attach(document.body);
resizefunc.push("initscrolls");
resizefunc.push("changeptype");
$('.animate-number').each(function(){
$(this).animateNumbers($(this).attr("data-value"), true, parseInt($(this).attr("data-duration")));
});
//RUN RESIZE ITEMS
$(window).resize(debounce(resizeitems,100));
$("body").trigger("resize");
// right side-bar toggle
$('.right-bar-toggle').on('click', function(e){
$('#wrapper').toggleClass('right-bar-enabled');
});
},
//initilizing
App.prototype.init = function() {
var $this = this;
//document load initialization
$(document).ready($this.onDocReady);
//init side bar - left
$.Sidemenu.init();
//init fullscreen
$.FullScreen.init();
},
$.App = new App, $.App.Constructor = App
}(window.jQuery),
//initializing main application module
function($) {
"use strict";
$.App.init();
}(window.jQuery);
/* ------------ some utility functions ----------------------- */
//this full screen
var toggle_fullscreen = function () {
}
function executeFunctionByName(functionName, context /*, args */) {
var args = [].slice.call(arguments).splice(2);
var namespaces = functionName.split(".");
var func = namespaces.pop();
for(var i = 0; i < namespaces.length; i++) {
context = context[namespaces[i]];
}
return context[func].apply(this, args);
}
var w,h,dw,dh;
var changeptype = function(){
w = $(window).width();
h = $(window).height();
dw = $(document).width();
dh = $(document).height();
if(jQuery.browser.mobile === true){
$("body").addClass("mobile").removeClass("fixed-left");
}
if(!$("#wrapper").hasClass("forced")){
if(w > 990){
$("body").removeClass("smallscreen").addClass("widescreen");
$("#wrapper").removeClass("enlarged");
}else{
$("body").removeClass("widescreen").addClass("smallscreen");
$("#wrapper").addClass("enlarged");
$(".left ul").removeAttr("style");
}
if($("#wrapper").hasClass("enlarged") && $("body").hasClass("fixed-left")){
$("body").removeClass("fixed-left").addClass("fixed-left-void");
}else if(!$("#wrapper").hasClass("enlarged") && $("body").hasClass("fixed-left-void")){
$("body").removeClass("fixed-left-void").addClass("fixed-left");
}
}
toggle_slimscroll(".slimscrollleft");
}
var debounce = function(func, wait, immediate) {
var timeout, result;
return function() {
var context = this, args = arguments;
var later = function() {
timeout = null;
if (!immediate) result = func.apply(context, args);
};
var callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow) result = func.apply(context, args);
return result;
};
}
function resizeitems(){
if($.isArray(resizefunc)){
for (i = 0; i < resizefunc.length; i++) {
window[resizefunc[i]]();
}
}
}
function initscrolls(){
if(jQuery.browser.mobile !== true){
//SLIM SCROLL
$('.slimscroller').slimscroll({
height: 'auto',
size: "7px"
});
$('.slimscrollleft').slimScroll({
height: 'auto',
position: 'right',
size: "7px",
color: '#828e94',
wheelStep: 5
});
}
}
function toggle_slimscroll(item){
if($("#wrapper").hasClass("enlarged")){
$(item).css("overflow","inherit").parent().css("overflow","inherit");
$(item). siblings(".slimScrollBar").css("visibility","hidden");
}else{
$(item).css("overflow","hidden").parent().css("overflow","hidden");
$(item). siblings(".slimScrollBar").css("visibility","visible");
}
}
// === following js will activate the menu in left side bar based on url ====
$(document).ready(function() {
$("#sidebar-menu a").each(function() {
var pageUrl = window.location.href.split(/[?#]/)[0];
if (this.href == pageUrl) {
$(this).addClass("active");
$(this).parent().addClass("active"); // add active to li of the current link
$(this).parent().parent().prev().addClass("active"); // add active class to an anchor
$(this).parent().parent().prev().click(); // click the item to make it drop
}
});
});
var resizefunc = [];
This could because of the redraw event not firing on the browser implicitly after your DOM manipulation. Refer this post on how to redraw so your generated DOM is refreshed on the UI
Force DOM redraw/refresh on Chrome/Mac
If this doesn't work please share your CSS and the before DOM and after DOM generation screenshot

Event isn't getting removed

I have a myDiv.mousedown, window.mousemove, and window.mouseup event. I'm trying to organize my code, and have it clean. So I created a function and returned the 3 event handlers.
The mousedown event adds a mousemove event, and the mouseup event removes the mousemove event.
The problem is, the mousemove event never gets removed. The mouseup event does get fired, but the mousemove event stays. What am I doing wrong, and how can I fix it (in a clean and organized way)?
JSFiddle
var myDiv = document.getElementById('myDiv');
var VeryUsefullClass = (function() {
function VeryUsefullClass(parentElem, options) {
var _this = this;
_this.index = 0;
_this.mouseHandlerBinded = mouseHandler.bind(_this);
myDiv.addEventListener('mousedown', _this.mouseHandlerBinded().mouseDown);
window.addEventListener('mouseup', _this.mouseHandlerBinded().mouseUp);
}
function mouseHandler() {
var _this = this;
var obj = {
mouseDown: function(e) {
console.log('mouseDown');
window.addEventListener('mousemove', _this.mouseHandlerBinded().mouseMove);
},
mouseMove: function(e) {
myDiv.innerHTML = 'mouseMove ' + _this.index++;
},
mouseUp: function(e) {
console.log('mouseUp');
_this.index = 0;
window.removeEventListener('mousemove', _this.mouseHandlerBinded().mouseMove);
}
}
return obj;
}
return VeryUsefullClass;
})();
console.clear();
var hello = new VeryUsefullClass();
#myDiv {
position: absolute;
background-color: orange;
width: 200px;
height: 100px;
}
<div id="myDiv"></div>
_this.mouseHandlerBinded().mouseMove , you will get the function which is a new function , never binded .
var myDiv = document.getElementById('myDiv');
var VeryUsefullClass = (function() {
var mdown= mouseHandler().mouseDown;
var mup = mouseHandler().mouseUp;
var mmove; // unbind with this function will work
function VeryUsefullClass(parentElem, options) {
var _this = this;
_this.index = 0;
mmove = mouseHandler.call(_this).mouseMove;
myDiv.addEventListener('mousedown', mdown);
window.addEventListener('mouseup', mup);
}
function mouseHandler() {
var _this = this;
var obj = {
mouseDown: function(e) {
console.log('mouseDown');
window.addEventListener('mousemove', mmove);
},
mouseMove: function(e) {
myDiv.innerHTML = 'mouseMove ' + (_this.index++);
},
mouseUp: function(e) {
console.log('mouseUp');
_this.index = 0;
window.removeEventListener('mousemove', mmove);
}
}
return obj;
}
return VeryUsefullClass;
})();
console.clear();
var hello = new VeryUsefullClass();

Resume a function after clearInterval

I have this code:
jQuery(function($) { // DOM is ready
var $el = $("header tr"),
tot = $el.length,
c = 0;
var timer = setInterval(function() {
$el.removeClass("current").eq(++c % tot).addClass("current");
}, 3000);
$el.first().addClass("current");
$el.on({
mouseenter: function(e) {
clearInterval(timer);
}
});
$el.mouseout({
timer;
});
});
I want to suspend the function on mouseover and resume it on mouse out but I cant get the latter right. How can I resume it?
Thank you.
There are two ways:
Set a flag that the function being called by the interval checks, and have the function not do anything if it's "suspended."
Start the interval again via a new setInterval call. Note that the old timer value cannot be used for this, you need to pass in the code again.
Example of #1:
jQuery(function($) { // DOM is ready
var $el = $("header tr"),
tot = $el.length,
c = 0,
suspend = false; // The flag
var timer = setInterval(function() {
if (!suspend) { // Check it
$el.removeClass("current").eq(++c % tot).addClass("current");
}
}, 3000);
$el.first().addClass("current");
$el.on({
mouseenter: function(e) {
suspend = true; // Set it
},
mouseleave: function(e) {
suspend = false; // Clear it
}
});
});
Example of #2:
jQuery(function($) { // DOM is ready
var $el = $("header tr"),
tot = $el.length,
c = 0,
timer = 0;
// Move this to a reusable function
var intervalHandler = function() {
$el.removeClass("current").eq(++c % tot).addClass("current");
};
// Probably best to encapsulate the logic for starting it rather
// than repeating that logic
var startInterval = function() {
timer = setInterval(intervalHandler, 3000);
};
// Initial timer
startInterval();
$el.first().addClass("current");
$el.on({
mouseenter: function(e) {
clearInterval(timer); // Stop it
}
mouseleave: function(e) {
startInterval(); // Start it
}
});
});
Checkout these prototypes:
//Initializable
function Initializable(params) {
this.initialize = function(key, def, private) {
if (def !== undefined) {
(!!private ? params : this)[key] = (params[key] !== undefined) ? params[key] : def;
}
};
}
function PeriodicJobHandler(params) {
Initializable.call(this, params);
this.initialize("timeout", 1000, true);
var getTimeout = function() {
return params.timeout;
};
var jobs = [];
function Job(params) {
//expects params.job() function
Initializable.call(this, params);
this.initialize("timeout", getTimeout(), true);
this.initialize("instant", false);
var intervalID = undefined;
this.start = function() {
if (intervalID !== undefined) {
return;
}
if (this.instant) {
params.job(true);
}
intervalID = setInterval(function() {
params.job(false);
}, params.timeout);
};
this.stop = function() {
clearInterval(intervalID);
intervalID = undefined;
};
}
this.addJob = function(params) {
jobs.push(new Job(params));
return jobs.length - 1;
};
this.removeJob = function(index) {
jobs[index].stop();
jobs.splice(index, 1);
};
this.startJob = function(index) {
jobs[index].start();
};
this.stopJob = function(index) {
jobs[index].stop();
};
}
Initializable simplifies member initialization, while PeriodicJobHandler is able to manage jobs in a periodic fashion. Now, let's use it practically:
var pjh = new PeriodicJobHandler({});
//It will run once/second. If you want to change the interval time, just define the timeout property in the object passed to addJob
var jobIndex = pjh.addJob({
instant: true,
job: function() {
$el.removeClass("current").eq(++c % tot).addClass("current");
}
});
jQuery(function($) { // DOM is ready
var $el = $("header tr"),
tot = $el.length,
c = 0;
var timer = setInterval(function() {
$el.removeClass("current").eq(++c % tot).addClass("current");
}, 3000);
$el.first().addClass("current");
$el.on({
mouseenter: function(e) {
jobIndex.stop();
}
});
$el.mouseout({
jobIndex.start();
});
});
With Javascript, it is much easy and efficient.
You can change the interval in setInterval function.
It is checking whether suspend variable is false or true, here suspend variable is setting to true, if mouseEnter function is called and set to false if mouseLeave function is called.
var displayMsg = document.getElementById('msg').innerHTML;
var i = 0;
var suspend = false;
var sequence = setInterval(update, 100);
function update() {
if (suspend == false) {
var dispalyedMsg = '';
dispalyedMsg = displayMsg.substring(i, displayMsg.length);
dispalyedMsg += ' ';
dispalyedMsg += displayMsg.substring(0, i);
document.getElementById('msg').innerHTML = dispalyedMsg;
i++;
if (i > displayMsg.length) {
i = 0;
}
}
}
document.getElementById('msg').addEventListener('mouseenter', mouseEnter);
document.getElementById('msg').addEventListener('mouseleave', mouseLeave);
function mouseEnter() {
suspend = true;
}
function mouseLeave() {
suspend = false;
}
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
#msg {
width: 680px;
height: 100px;
border: 1px solid #ccc;
padding-left: 15px;
}
</style>
</head>
<body>
<div id="msg">
Today is only 15% discount. Hurry up to grab. Sale will end sooooooooooooon!!!!
</div>
<div id="output"></div>
<script src="marquee.js"></script>
</body>
</html>

Cursor pointer moves to first position onclick textarea using CK-Editor

I am using CK-Editor 4. In which I have created Directive. When I click on Textarea my cursor pointer is Move to First position. I want my cursor position should be available when I click in any of the Place inside Textarea. It should not be able to move first or last position until I click on last position.
vpb.directive('ckeInline', function ($timeout, $parse, $rootScope,$) {
return {
require: "ngModel",
link: function (scope, element, attrs, ngModel) {
if (attrs.ckeInlineShowtitle) ngModel = "";
isMobile = $(".ipad, .ipod, .android").length > 0;
var destroy_this_editor = function(id)
{
setTimeout(function () {
try{
CKEDITOR.instances[id].destroy(true);
} catch (ex) {
}
},500)
}
if (!isMobile) {
var create_and_focus = function () {
if (scope.setup.edit_mode || attrs.ckeNonEditMode) {
attrs.$set('contenteditable', true);
var a_id = attrs.ckeInlineId;
var menu_selector = attrs.ckeInlineMenu;
//get editor
//create editor if doesn't exist
var e = CKEDITOR.instances[a_id];
if (!e) {
element.menu = $(menu_selector);
//set up behavior for menu
uif.wire_up_menu(element, scope);
//hide all menu bars
$("nav.text-editor-menu-bar").hide();
//create editor
var config = {
toolbar: "More",
on: {
'blur': function () {
save_editor_content(function () {
destroy_this_editor(a_id);
});
},
'focusout': function () {
save_editor_content(function () {
destroy_this_editor(a_id);
});
},
'focus': function () {
//show the current menu
element.menu.show();
//fix menu width
uif.stretch_menu(element);
},
'instanceReady': function (event,element) {
//----------------I think,Here i want logic to set caret sign or set mouse
//pointer to appropriate place
// event.editor.focus();
// element.focus();
}
}
};
if (attrs.ckeInlineToolbar) {
config.toolbar = attrs.ckeInlineToolbar;
}
var editor = CKEDITOR.inline(a_id, config);
if (attrs.ckeInlineOnInit) {
uif.apply_scope($rootScope, $parse(attrs.ckeInlineOnInit));
}
}
else
{
e.focus();
element.focus();
}
} else
{
attrs.$set('contenteditable', false);
}
}
element.click(function () {
create_and_focus();
});
if (attrs.ckeInlineFocusWatch) {
scope.$watch(function () {
return $parse(attrs.ckeInlineFocusWatch);
}, function () {
if (attrs.ckeInlineFocusCondition == undefined || $parse(attrs.ckeInlineFocusCondition)() == true) {
create_and_focus();
}
})
}
var saving = false;
var save_editor_content = function (cb) {
if (!saving)
{
saving = true;
var a_id = attrs.ckeInlineId;
if (a_id) {
var editor = CKEDITOR.instances[a_id];
if (editor) {
var menu_selector = attrs.ckeInlineMenu;
element.menu.hide();
//attrs.$set('contenteditable', false);
var newValue = editor.getData().replace(/ /, ' ');
if (ngModel.$modelValue != newValue) {
if (attrs.ckeInlineBlurBool) {
$parse(attrs.ckeInlineBlurBool).assign($rootScope, false);
}
ngModel.$setViewValue(newValue);
if (attrs.ckeInlineSave) {
uif.apply_scope(scope, $parse(attrs.ckeInlineSave));
}
$timeout(function () {
saving = false;
if (cb) cb();
}, 1100)
} else
{
saving = false;
$rootScope.setup.blcok_edits = false;
$rootScope.setup.block_all_editors = false;
if (cb) cb();
}
}
}
else
{
saving = false;
}
}
};
} else if (attrs.ckeNonEditMode) {
attrs.$set('contenteditable', true);
element.blur(function () {
ngModel.$setViewValue(element.html().replace(/ /, ' '));
if (attrs.ckeInlineSave) {
uif.apply_scope(scope, $parse(attrs.ckeInlineSave));
}
})
}
}
}
});

$window.height is not a function Firefox

I am using infinite scroll directive and this is the code:
angApp.directive('infiniteScroll', [
'$rootScope', '$window', '$timeout', function ($rootScope, $window, $timeout) {
return {
link: function (scope, elem, attrs) {
var checkWhenEnabled, handler, scrollDistance, scrollEnabled;
$window = angular.element($window);
scrollDistance = 0;
if (attrs.infiniteScrollDistance != null) {
scope.$watch(attrs.infiniteScrollDistance, function (value) {
return scrollDistance = parseInt(value, 10);
});
}
scrollEnabled = true;
checkWhenEnabled = false;
if (attrs.infiniteScrollDisabled != null) {
scope.$watch(attrs.infiniteScrollDisabled, function (value) {
scrollEnabled = !value;
if (scrollEnabled && checkWhenEnabled) {
checkWhenEnabled = false;
return handler();
}
});
}
handler = function () {
var elementBottom, remaining, shouldScroll, windowBottom;
console.log($window);
windowBottom = $window.height() + $window.scrollTop();
elementBottom = elem.offset().top + elem.height();
remaining = elementBottom - windowBottom;
shouldScroll = remaining <= $window.height() * scrollDistance;
if (shouldScroll && scrollEnabled) {
if ($rootScope.$$phase) {
return scope.$eval(attrs.infiniteScroll);
} else {
return scope.$apply(attrs.infiniteScroll);
}
} else if (shouldScroll) {
return checkWhenEnabled = true;
}
};
$window.on('scroll', handler);
scope.$on('$destroy', function () {
return $window.off('scroll', handler);
});
return $timeout((function () {
if (attrs.infiniteScrollImmediateCheck) {
if (scope.$eval(attrs.infiniteScrollImmediateCheck)) {
return handler();
}
} else {
return handler();
}
}), 0);
}
};
}
]);
The problem with this code is sometimes it works sometimes it doesn't. If I do a hard refresh by pressing Ctrl + F5 it most certainly throws the below error.
This is the error:
I am using Firefox 29.0.1. What am I missing?
Normally this should work, but there might be some problem with requireJS. Using $() instead should ensure you are using Jquery. You might need the order plug-in for requireJS since normally RequireJS loads and evaluates scripts in an undetermined order.

Categories

Resources