Combining <ons-sliding-menu> and <ons-carousel> - onsen-ui

I have an app with <ons-sliding-menu> and a page with <ons-toolbar> and a horizontal <ons-carousel> covering the remaining space.
For the <ons-sliding-menu> the parameter swipe-target-width="50px" is set.
Is there a way to tell the <ons-carousel> to ignore events originating from the most left 50px and let these go to the menu?

Currently there is no option to make the carousel ignore events on one side, but perhaps you can make a trick. You can put a div at the same level than the carousel and let it take the clicks instead of the carousel in the area you need:
<div class="cover"></div>
<ons-carousel>
...
</ons-carousel>
You can change these values to fit your case:
.cover {
position: absolute;
left: 0;
height: 100%;
width: 200px;
z-index: 1;
}
Check it out here: http://codepen.io/frankdiox/pen/YqKOJE
Hope it helps!

After some experimentation, I came to the solution to inject the necessary functionality directly in the drag event handlers of the OnsCarouselElement. For this purpose I have introduced the attribute swipe-ignore-left for the <ons-carousel>. The other sites could easily be added when needed.In order to inject the functionality, load this JS-Code after loading onsenui.js:
(function () {
'use strict';
/****************************************************************
Checks the current event against the attribute swipe-ignore-left.
****************************************************************/
window.OnsCarouselElement.prototype._ignoreDrag = function (event) {
var attr = this.getAttribute('swipe-ignore-left');
if (attr === undefined) return false;
var left = parseInt(attr, 10);
if (left === undefined || left < 1) return false;
var startX = event.gesture.center.clientX - event.gesture.deltaX;
return startX < left;
};
/****************************************************************
Save the original drag-event-handlers
****************************************************************/
var originalCarouselOnDrag = window.OnsCarouselElement.prototype._onDrag;
var originalCarouselOnDragEnd = window.OnsCarouselElement.prototype._onDragEnd;
/****************************************************************
Override: OnsCarouselElement.prototype._onDrag
****************************************************************/
window.OnsCarouselElement.prototype._onDrag = function (event) {
if (this._ignoreDrag(event)) return;
originalCarouselOnDrag.apply(this, arguments);
};
/****************************************************************
Override: OnsCarouselElement.prototype._onDragEnd
****************************************************************/
window.OnsCarouselElement.prototype._onDragEnd = function (event) {
if (this._ignoreDrag(event)) return;
originalCarouselOnDragEnd.apply(this, arguments);
};
})();
To preserve for example the left 20 pixel for the <ons-sliding-menu>, this HTML is to provide:
<ons-sliding-menu ... side="left" swipeable swipe-target-width="20px" />
...
<ons-carousel ... swipeable swipe-ignore-left="20px" />

Related

How to close component via changing the state [duplicate]

I'm currently using jQuery to make a div clickable and in this div I also have anchors. The problem I'm running into is that when I click on an anchor both click events are firing (for the div and the anchor). How do I prevent the div's onclick event from firing when an anchor is clicked?
Here's the broken code:
JavaScript
var url = $("#clickable a").attr("href");
$("#clickable").click(function() {
window.location = url;
return true;
})
HTML
<div id="clickable">
<!-- Other content. -->
I don't want #clickable to handle this click event.
</div>
Events bubble to the highest point in the DOM at which a click event has been attached. So in your example, even if you didn't have any other explicitly clickable elements in the div, every child element of the div would bubble their click event up the DOM to until the DIV's click event handler catches it.
There are two solutions to this is to check to see who actually originated the event. jQuery passes an eventargs object along with the event:
$("#clickable").click(function(e) {
var senderElement = e.target;
// Check if sender is the <div> element e.g.
// if($(e.target).is("div")) {
window.location = url;
return true;
});
You can also attach a click event handler to your links which tell them to stop event bubbling after their own handler executes:
$("#clickable a").click(function(e) {
// Do something
e.stopPropagation();
});
Use stopPropagation method, see an example:
$("#clickable a").click(function(e) {
e.stopPropagation();
});
As said by jQuery Docs:
stopPropagation method prevents the event from bubbling up the DOM
tree, preventing any parent handlers from being notified of the event.
Keep in mind that it does not prevent others listeners to handle this event(ex. more than one click handler for a button), if it is not the desired effect, you must use stopImmediatePropagation instead.
Here my solution for everyone out there looking for a non-jQuery code (pure javascript)
document.getElementById("clickable").addEventListener("click", function(e) {
e = window.event || e;
if(this === e.target) {
// put your code here
}
});
Your code wont be executed if clicked on parent's children
If you do not intend to interact with the inner element/s in any case, then a CSS solution might be useful for you.
Just set the inner element/s to pointer-events: none
in your case:
.clickable > a {
pointer-events: none;
}
or to target all inner elements generally:
.clickable * {
pointer-events: none;
}
This easy hack saved me a lot of time while developing with ReactJS
Browser support could be found here: http://caniuse.com/#feat=pointer-events
Inline Alternative:
<div>
<!-- Other content. -->
<a onclick='event.stopPropagation();' href="http://foo.example">I don't want #clickable to handle this click event.</a>
</div>
You can also try this
$("#clickable").click(function(event) {
var senderElementName = event.target.tagName.toLowerCase();
if(senderElementName === 'div') {
// Do something here
} else {
// Do something with <a> tag
}
});
Writing if anyone needs (worked for me):
event.stopImmediatePropagation()
From this solution.
Using return false; or e.stopPropogation(); will not allow further code to execute. It will stop flow at this point itself.
If you have multiple elements in the clickable div, you should do this:
$('#clickable *').click(function(e){ e.stopPropagation(); });
I compare to ev.currentTarget when this is not available (React, etc).
$("#clickable").click(function(e) {
if (e.target === e.currentTarget) {
window.location = url;
return true;
}
})
Here's an example using Angular 2+
For example, if you wanted to close a Modal Component if the user clicks outside of it:
// Close the modal if the document is clicked.
#HostListener('document:click', ['$event'])
public onDocumentClick(event: MouseEvent): void {
this.closeModal();
}
// Don't close the modal if the modal itself is clicked.
#HostListener('click', ['$event'])
public onClick(event: MouseEvent): void {
event.stopPropagation();
}
If it is in inline context, in HTML try this:
onclick="functionCall();event.stopPropagation();
e.stopPropagation() is a correct solution, but in case you don't want to attach any event handler to your inner anchor, you can simply attach this handler to your outer div:
e => { e.target === e.currentTarget && window.location = URL; }
var inner = document.querySelector("#inner");
var outer = document.querySelector("#outer");
inner.addEventListener('click',innerFunction);
outer.addEventListener('click',outerFunction);
function innerFunction(event){
event.stopPropagation();
console.log("Inner Functiuon");
}
function outerFunction(event){
console.log("Outer Functiuon");
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>Pramod Kharade-Event with Outer and Inner Progration</title>
</head>
<body>
<div id="outer" style="width:100px;height:100px;background-color:green;">
<div id="inner" style="width:35px;height:35px;background-color:yellow;"></div>
</div>
</body>
</html>
You need to stop the event from reaching (bubbling to) the parent (the div).
See the part about bubbling here, and jQuery-specific API info here.
To specify some sub element as unclickable write the css hierarchy as in the example below.
In this example I stop propagation to any elements (*) inside td inside tr inside a table with the class ".subtable"
$(document).ready(function()
{
$(".subtable tr td *").click(function (event)
{
event.stopPropagation();
});
});
You can check whether the target is not your div-element and then issue another click event on the parent after which you will "return" from the handle.
$('clickable').click(function (event) {
let div = $(event.target);
if (! div.is('div')) {
div.parent().click();
return;
}
// Then Implement your logic here
}
Here is a non jQuery solution that worked for me.
<div style="background:cyan; width:100px; height:100px;" onclick="if (event.srcElement==this) {console.log('outer');}">
<a style="background:red" onclick="console.log('inner');">Click me</a>
</div>
for those that are not using jQuery
document.querySelector('.clickable').addEventListener('click', (e) =>{
if(!e.target.classList.contains('clickable')) return
// place code here
})
In case someone had this issue using React, this is how I solved it.
scss:
#loginBackdrop {
position: absolute;
width: 100% !important;
height: 100% !important;
top:0px;
left:0px;
z-index: 9; }
#loginFrame {
width: $iFrameWidth;
height: $iFrameHeight;
background-color: $mainColor;
position: fixed;
z-index: 10;
top: 50%;
left: 50%;
margin-top: calc(-1 * #{$iFrameHeight} / 2);
margin-left: calc(-1 * #{$iFrameWidth} / 2);
border: solid 1px grey;
border-radius: 20px;
box-shadow: 0px 0px 90px #545454; }
Component's render():
render() {
...
return (
<div id='loginBackdrop' onClick={this.props.closeLogin}>
<div id='loginFrame' onClick={(e)=>{e.preventDefault();e.stopPropagation()}}>
... [modal content] ...
</div>
</div>
)
}
By a adding an onClick function for the child modal (content div) mouse click events are prevented to reach the 'closeLogin' function of the parent element.
This did the trick for me and I was able to create a modal effect with 2 simple divs.
If a child element is clicked, then the event bubbles up to the parent and event.target !== event.currentTarget.
So in your function, you can check this and return early, i.e.:
var url = $("#clickable a").attr("href");
$("#clickable").click(function(event) {
if ( event.target !== event.currentTarget ){
// user clicked on a child and we ignore that
return;
}
window.location = url;
return true;
})
This is what you are looking for
mousedown event. this works on every DOM elements to prevent javascript focus handler like this:
$('.no-focus').mousedown(function (e) {
e.prevenDefault()
// do stuff
}
in vue.js framework, you can use modifier like this:
<span #mousedown.prevent> no focus </span>
Note that using on the input will prevent text selection handler
add a as follows:
....
or return false; from click handler for #clickable like:
$("#clickable").click(function() {
var url = $("#clickable a").attr("href");
window.location = url;
return false;
});
All solution are complicated and of jscript. Here is the simplest version:
var IsChildWindow=false;
function ParentClick()
{
if(IsChildWindow==true)
{
IsChildWindow==false;
return;
}
//do ur work here
}
function ChildClick()
{
IsChildWindow=true;
//Do ur work here
}
<a onclick="return false;" href="http://foo.example">I want to ignore my parent's onclick event.</a>

how to implement Draggable element in ionic v1

Hi Guys My requirement is to make an element draggable across the screen and set the position of the element to the place where users stops dragging. So far I am able to make a element draggable across the screen but once released it is going back to its old position (position where it was earlier) I am using ngDraggable directive of angular. Sorry I am new to Ionic and angular. Any help will be highly appreciated.
My code goes as follows
<div ng-drag="true" id="draggableAxis" ng-style="{{draggedStyle}}" ng-drag-success="onDragComplete($data,$event)">
<img src="img/axis.png" >
<img ng-src="img/other.png" style="width:75px">
</div>
In my controller:
$scope.draggedStyle = {top: '96px',
right: '90px'};
var onDraggableEvent = function(evt, data) {
if(evt.name=='draggable:start'){
console.log('draggable-start');
console.log(data.x);
console.log(data.y);
}else{
console.log('draggable-end');
console.log(data);
console.log(data.element.centerX +' '+data.element.centerY);
console.log(evt);
$scope.setPosition(data);
}
}
$scope.onDragComplete=function(data,evt){
console.log("drag success, data:", data);
console.log(evt);
}// this fn doesnot gets triggered
$scope.$on('draggable:start', onDraggableEvent);
$scope.$on('draggable:end', onDraggableEvent);
$scope.setPosition=function(data){
$scope.draggedStyle = {
top: data.x+'px',
right: data.y+'px'
};
}
SCREEN SHOT OF MOBILE VIEW
I updated the fix to https://plnkr.co/edit/fVaIUVvAd7jLETkwVepm?p=preview
One of the problems was
ng-style="{{draggedStyle}}"
which should be
ng-style="draggedStyle"
Also, I switched the setPosition method to flip the x and y and used left because x indicates position from the left and not right.
$scope.setPosition = function(data) {
$scope.draggedStyle = {
top: data.y + 'px',
left: data.x + 'px'
};
}
Hope that helps

When zooming, circle gets 'dragged' from original position

I'm using mapbox.js and map.css to create a map with a simple circle drawn on it, and nothing else. (working jsfiddle) On the example that I did without angular, the zoom works as it should (meaning, it stays on the same position when zooming).
When I integrated with the example and I zoom, the circle gets dragged slightly to the top left when zooming out and to the bottom right when I zoom in, in the end returning to the original position when the zoom stops.
On the angular project I'm using require to load the scripts to the page and the view that has the map is not the first one that is loaded. I am not using a directive for leaflet, just using the files. The code is exactly the same from the example to the angular project, the only difference is that I am not using a script tag in the html file as I was in the example (since the code has been moved to the controller).
I wanted the behaviour to be the same as it was in the example but at this stage I don't know what might be causing it besides the fact that is not the view that is first being loaded.
Here's the view code:
<div id="leafletMap" style="width: 500px; height: 300px;"></div>
Here's the code in my controller:
/* globals L, $ */
define(['angular'], function (angular) {
'use strict';
/**
* #ngdoc function
* #name rmsPortalApp.controller:ViewMapCtrl
* #description
* # ViewMapCtrl
* Controller of the rmsPortalApp
*/
angular.module('rmsPortalApp.controllers.ViewMapCtrl', [])
.controller('ViewMapCtrl', function ($scope, Data) {
var ownerCircleLayer;
L.Map = L.Map.extend({
openPopup: function (popup) {
// this.closePopup(); // just comment this
this._popup = popup;
return this.addLayer(popup).fire('popupopen', {
popup: this._popup
});
}
});
var map = L.map('leafletMap', {
touchZoom: true,
dragging: true
}).setView([39.678, -8.229], 6);
/* Initialize the SVG layer */
//map._initPathRoot();
/* SVG from the map object */
//var svg = d3.select("#map").select("svg");
var southWest = [85.05, -180.01];
var northEast = [-85.06, 180.05];
var bounds = L.latLngBounds(southWest, northEast);
map.setMaxBounds(bounds);
L.tileLayer('http://api.tiles.mapbox.com/v4/bmpgp.010fd6a5/{z}/{x}/{y}.png?access_token=pk.eyJ1IjoiYm1wZ3AiLCJhIjoiOWY4NGYwN2VjZDg0MGI1ZjdmMWI3ZjdlNGNmY2NmNmQifQ.FZ5cr4mO3iDKVkx9zz4Nkg', {
attribution: '© Powered By CGI',
minZoom: 3,
maxZoom: 18,
id: 'bmpgp.010fd6a5',
accessToken: 'pk.eyJ1IjoiYm1wZ3AiLCJhIjoiOWY4NGYwN2VjZDg0MGI1ZjdmMWI3ZjdlNGNmY2NmNmQifQ.FZ5cr4mO3iDKVkx9zz4Nkg'
}).addTo(map);
setOwnerCircle()
function setOwnerCircle() {
ownerCircleLayer = new L.layerGroup();
var ownerCircle = L.circle([39, -8], 3000, {
color: 'red',
opacity: 1,
weigth: 1,
fillOpacity: 0.0,
className: 'leaflet-zoom-animated'
});
ownerCircleLayer.addLayer(ownerCircle);
var circleBounds = ownerCircle.getBounds();
// Image popup
var endLat = circleBounds.getCenter().lat - 0.10000;
var endLon = circleBounds.getEast();
map.addLayer(ownerCircleLayer);
addedOwnerCircle = true;
}
})
});
EDIT:
I have tried putting it in the first view that is being loaded, it still happens.
Found the reason why it was happening:
When double checking the actual mapbox.js and mapbox.css I noticed that my css was wrong, upon replacing it with the right mapbox.css it stopped doing the dragging. Thank you for the help regardless.

How do I capture table td elements using mousedown.dragselect event?

I have a directive which renders a HTML table where each td element has an id
What I want to accomplish is to use the mousedown.dragselect/mouseup.dragselect to determine which elements have been selected, and then highlight those selected elements. What I have so far is something like this:
var $ele = $(this);
scope.bindMultipleSelection = function() {
element.bind('mousedown.dragselect', function() {
$document.bind('mousemove.dragselect', scope.mousemove);
$document.bind('mouseup.dragselect', scope.mouseup);
});
};
scope.bindMultipleSelection();
scope.mousemove = function(e) {
scope.selectElement($(this));
};
scope.mouseup = function(e) {
};
scope.selectElement = function($ele) {
if (!$ele.hasClass('eng-selected-item'))
$ele.addClass('eng-selected-item'); //apply selection or de-selection to current element
};
How can I get every td element selected by mousedown.dragselect, and be able to get their ids and then highlight them?
I suspect using anything relating to dragging won't give you what you want. Dragging is actually used when moving elements about (e.g. dragging files in My Computer / Finder), when what you're after is multiple selection.
So there a number of things the directive needs:
Listen to mousedown, mouseenter and mouseup, events.
mousedown should listen on the cells of the table, and set a "dragging" mode.
mouseenter should listen on the cells as well, and if the directive is in dragging mode, select the "appropriate cells"
mouseup should disable dragging mode, and actually be on the whole body, in case the mouse is lifted up while the cursor is not over the table.
jQuery delegation is useful here, as it can nicely delegate the above events to the table, so the code is much more friendly to cells that are added after this directive is initialised. (I wouldn't include or use jQuery in an Angular project unless you have a clear reason like this).
Although you've not mentioned it, the "appropriate cells" I suspect all the cells "between" where the mouse was clicked, and the current cell, chosen in a rectangle, and not just the cells that have been entered while the mouse was held down. To find these, cellIndex and rowIndex can be used, together with filtering all the cells from the table.
All the listeners should be wrapped $scope.$apply to make sure Angular runs a digest cycle after they fire.
For the directive to communicate the ids of the selected elements to the surrounding scope, the directive can use bi-directional binding using the scope property, and the = symbol, as explained in the Angular docs
Putting all this together gives:
app.directive('dragSelect', function($window, $document) {
return {
scope: {
dragSelectIds: '='
},
controller: function($scope, $element) {
var cls = 'eng-selected-item';
var startCell = null;
var dragging = false;
function mouseUp(el) {
dragging = false;
}
function mouseDown(el) {
dragging = true;
setStartCell(el);
setEndCell(el);
}
function mouseEnter(el) {
if (!dragging) return;
setEndCell(el);
}
function setStartCell(el) {
startCell = el;
}
function setEndCell(el) {
$scope.dragSelectIds = [];
$element.find('td').removeClass(cls);
cellsBetween(startCell, el).each(function() {
var el = angular.element(this);
el.addClass(cls);
$scope.dragSelectIds.push(el.attr('id'));
});
}
function cellsBetween(start, end) {
var coordsStart = getCoords(start);
var coordsEnd = getCoords(end);
var topLeft = {
column: $window.Math.min(coordsStart.column, coordsEnd.column),
row: $window.Math.min(coordsStart.row, coordsEnd.row),
};
var bottomRight = {
column: $window.Math.max(coordsStart.column, coordsEnd.column),
row: $window.Math.max(coordsStart.row, coordsEnd.row),
};
return $element.find('td').filter(function() {
var el = angular.element(this);
var coords = getCoords(el);
return coords.column >= topLeft.column
&& coords.column <= bottomRight.column
&& coords.row >= topLeft.row
&& coords.row <= bottomRight.row;
});
}
function getCoords(cell) {
var row = cell.parents('row');
return {
column: cell[0].cellIndex,
row: cell.parent()[0].rowIndex
};
}
function wrap(fn) {
return function() {
var el = angular.element(this);
$scope.$apply(function() {
fn(el);
});
}
}
$element.delegate('td', 'mousedown', wrap(mouseDown));
$element.delegate('td', 'mouseenter', wrap(mouseEnter));
$document.delegate('body', 'mouseup', wrap(mouseUp));
}
}
});
Another thing that will make the experience a bit nicer, is to set the cursor to a pointer, and disable text selection
[drag-select] {
cursor: pointer;
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
You can also see this in action in this working demo

AngularJS ng-Grid and ngGridFlexibleHeightPlugin not working as I expect

I am trying to use Angular ng-grid to show some dynamic content. The grid will have 0 - 25 rows. I want to have a minimum size and then have the ng-grid auto adjust the height as items get added to the array. For some reason the auto size stops after adding 6-7 items (it even seems to depend on your resolution).
Can anyone help? What am I missing? I have a Plunker below that shows the issue.
http://plnkr.co/edit/frHbLTQ68GjMgzC59eSZ?p=preview
If you are still having issues I would suggestion not using the ng-grid and create your layout directly in html (Use DIVs, column width formatting, styles, etc...). Then populate your new layout using your $scope. Between variables, models and ng-repeat you should be able to do everything you need.
The code bellow is a modification to the plugin. It works by checking number of items in the grid data and calculating the height based on that. The options passed in to the plugin are height of row and header height.
ngGridCustomFlexibleHeightPlugin = function (opts) {
var self = this;
self.grid = null;
self.scope = null;
self.init = function (scope, grid, services) {
self.domUtilityService = services.DomUtilityService;
self.grid = grid;
self.scope = scope;
var recalcHeightForData = function () { setTimeout(innerRecalcForData, 1); };
var innerRecalcForData = function () {
var gridId = self.grid.gridId;
var footerPanelSel = '.' + gridId + ' .ngFooterPanel';
var extraHeight = self.grid.$topPanel.height() + $(footerPanelSel).height();
var naturalHeight = (grid.data.length - 1) * opts.rowHeight + opts.headerRowHeight;
self.grid.$viewport.css('height', (naturalHeight + 2) + 'px');
self.grid.$root.css('height', (naturalHeight + extraHeight + 2) + 'px');
// self.grid.refreshDomSizes();
if (!self.scope.$$phase) {
self.scope.$apply(function () {
self.domUtilityService.RebuildGrid(self.scope, self.grid);
});
}
else {
// $digest or $apply already in progress
self.domUtilityService.RebuildGrid(self.scope, self.grid);
}
};
scope.$watch(grid.config.data, recalcHeightForData);
};
};
I find using this piece of code on the stylesheet solved my problem. I disabled the plugin but it works either way.
.ngViewport.ng-scope{
height: auto !important;
overflow-y: hidden;
}
.ngTopPanel.ng-scope, .ngHeaderContainer{
width: auto !important;
}
.ngGrid{
background-color: transparent!important;
}
I hope it helps someone

Resources