Cannot create custom toolbar button that works on mobile - javascript

I have the simple code:
<button class="pswp__button" id="CustomButton" style="background:none;" title="Custom" onclick="CustomClicked()">
<img src="~/CustomImage.jpg"">
</button>
This works as expected on desktop browsers. When I switch to mobile though, I cannot get the event to fire when I tap the button. I've tried attaching every event handler I can think of: tap, click, touch, touchstart, etc, and I can still not get any of the events to fire on mobile.
If I invoke the click event through the console, it works on mobile as well, but I can't seem to get the click event to propagate to that control. I've tried setting its zindex to ridiculous values, and still no luck. Anyone have a clue?
After searching for some solutions online, they all seem to have teh same problem. This one, for instance, doesn't fire on mobile either:
var openPhotoSwipe = function() {
var pswpElement = document.querySelectorAll('.pswp')[0];
// build items array
var items = [
{
src: 'https://farm2.staticflickr.com/1043/5186867718_06b2e9e551_b.jpg',
w: 964,
h: 1024
},
{
src: 'https://farm7.staticflickr.com/6175/6176698785_7dee72237e_b.jpg',
w: 1024,
h: 683
}
];
// define options (if needed)
var options = {
// history & focus options are disabled on CodePen
history: false,
focus: false,
showAnimationDuration: 0,
hideAnimationDuration: 0
};
var gallery = new PhotoSwipe( pswpElement, PhotoSwipeUI_Default, items, options);
gallery.init();
};
openPhotoSwipe();
$(document).on('click', '.pswp__button.pswp__button--close' , function(){
alert('d'); // did not fire?
});
document.getElementById('btn').onclick = openPhotoSwipe;
<button id="btn">Open PhotoSwipe</button>
<!-- Root element of PhotoSwipe. Must have class pswp. -->
<div class="pswp" tabindex="-1" role="dialog" aria-hidden="true">
<!-- Background of PhotoSwipe.
It's a separate element, as animating opacity is faster than rgba(). -->
<div class="pswp__bg"></div>
<!-- Slides wrapper with overflow:hidden. -->
<div class="pswp__scroll-wrap">
<!-- Container that holds slides. PhotoSwipe keeps only 3 slides in DOM to save memory. -->
<div class="pswp__container">
<!-- don't modify these 3 pswp__item elements, data is added later on -->
<div class="pswp__item"></div>
<div class="pswp__item"></div>
<div class="pswp__item"></div>
</div>
<!-- Default (PhotoSwipeUI_Default) interface on top of sliding area. Can be changed. -->
<div class="pswp__ui pswp__ui--hidden">
<div class="pswp__top-bar">
<!-- Controls are self-explanatory. Order can be changed. -->
<div class="pswp__counter"></div>
<button class="pswp__button pswp__button--close" title="Close (Esc)"></button>
<button class="pswp__button pswp__button--share" title="Share"></button>
<button class="pswp__button pswp__button--fs" title="Toggle fullscreen"></button>
<button class="pswp__button pswp__button--zoom" title="Zoom in/out"></button>
<!-- Preloader demo http://codepen.io/dimsemenov/pen/yyBWoR -->
<!-- element will get class pswp__preloader--active when preloader is running -->
<div class="pswp__preloader">
<div class="pswp__preloader__icn">
<div class="pswp__preloader__cut">
<div class="pswp__preloader__donut"></div>
</div>
</div>
</div>
</div>
<div class="pswp__share-modal pswp__share-modal--hidden pswp__single-tap">
<div class="pswp__share-tooltip"></div>
</div>
<button class="pswp__button pswp__button--arrow--left" title="Previous (arrow left)">
</button>
<button class="pswp__button pswp__button--arrow--right" title="Next (arrow right)">
</button>
<div class="pswp__caption">
<div class="pswp__caption__center"></div>
</div>
</div>
</div>
</div>

In order to achieve this functionality, you must edit hte photoswipe-ui-default.js file.
Your custom button must have the class pswp__button--myCustomButtonName as well as pswp__button.
<button class="pswp__button pswp__button--myCustomButtonName" id="CustomButton" style="background:none;" title="Custom" onclick="CustomClicked()">
<img src="~/CustomImage.jpg"">
Inside the photoswipe-ui-default.js file, you'll see a list of controls:
var _uiElements = [
{
name: 'caption',
option: 'captionEl',
onInit: function(el) {
_captionContainer = el;
}
},
{
name: 'myCustomButton',
option: 'customOptionName',
onTap:MyCustomButtonFunction
},
You must then add that button name to the options:
var options = {
history: false,
escKey: false,
closeOnScroll: false,
pinchToClose: false,
closeOnVerticalDrag: false,
customOptionName: true
};

Use :before to apply additional styles to the buttons, or modify the default UI JS file, so it registers click on image too.
By default, it registers tap only on the target element (in your case it's image), and does not propagate up in the DOM tree.

Figured out a way that doesn't involve altering the library. It's just a bit unfortunate the way the click handling is written, since you cannot use preventDefault or stopPropagation to cancel the handler.
// Disable hiding controls when tapping action buttons
pswp.framework.bind(pswp.scrollWrap, 'pswpTap', (e) => {
let el = e.target || e.srcElement;
while ((el = el.parentElement) && !el.classList.contains('action'));
if (el) {
pswp.options.tapToToggleControls = false;
setTimeout(() => {
pswp.options.tapToToggleControls = true;
}, 50);
}
});

Related

Bootstrap 3 modal problem with data piling up

Having serious data problem.
I have a button, script action, bootstrap modal confirm.
Problem
Clicking delete and agreement on modal, works like a charm. One click, one network tab/delete action. But, clicking on "delete>cancel", "delete>cancel" etc. adds as many network tab/delete actions (check img)
NB! Problem is not the 404, but that there are two (or more) actions taken, even though cancel should remove previous info/data. There is no arrays, no modal leftovers, but once you click agreement OK, as many network actions/posts are made as many you cancelled previously. And weird part is that if you click other delete, it adds THAT as well, and therefor there will be multiple deletions. It's like adding elements to delete.
Code example here: https://jsfiddle.net/cvyw6758/4/
$(document).ready(function(){
$("#action_modal").on('show.bs.modal', function (event) {
let button_ = $(event.relatedTarget),
modal_ = $(this);
let id_ = (button_.data("row") ? button_.data("row") : ""),
question_ = (button_.data("modal_body") ? button_.data("modal_body") : ""),
title_ = (button_.data("modal_title") ? button_.data("modal_title") : "Agreement"),
action_ = (button_.data("action") ? button_.data("action") : false),
url_ = (button_.data("href") ? button_.data("href") : (button_.attr("href") ? button_.attr("href") : false));
modal_.find(".modal-title").html(title_);
// do confirm action
switch (action_) {
case "approve_remove":
const elem_ = button_.parent().parent();
modal_.find(".modal-body").html(`${question_}`);
$("#confirmed").on("click", function(e) {
e.preventDefault();
// do delete
$(this).off();
$.post(`${url_}${id_}`, function(data_) {
if(data_.status == "success") {
elem_.hide();
elem_.parent().next().html(`<td colspan="3">${data_.message}</td>`);
setTimeout(function() {
elem_.parent().parent().prev().slideUp("200").remove("");
elem_.parent().parent().slideUp("200").remove("");
}, 5000);
}
else {
$(`<tr><td colspan="3">${data_.message}</td></tr>`).insertBefore(elem_.parent());
setTimeout(function() {
elem_.parent().parent().prev().slideUp("200").remove();
}, 4000);
}
modal_.modal("hide");
}, "json");
});
break;
default:
console.log("No action assigned! Please inform IT department!");
modal_.find("#confirmed").hide();
modal_.find(".modal-body").html("No action assigned! Please inform IT department!");
break;
};
});
// reset modal
$("#action_modal").on('hidden.bs.modal', function (event) {
const modal_ = $(this);
modal_.removeData();
modal_.find(".modal-title").html("");
modal_.find(".modal-body").html("");
});
})
<script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
<!-- Latest compiled and minified CSS -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<!-- Optional theme -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css" integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp" crossorigin="anonymous">
<!-- Latest compiled and minified JavaScript -->
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
<!-- start of a modal -->
<div class="modal fade" id="action_modal" tabindex="-1" role="dialog" aria-labelledby="action_modal_label" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="action_modal_label">Popup</h5>
</div>
<div class="modal-body">
<h4 class="text-zenter"></h4>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-danger" data-dismiss="modal">cancel</button>
<button type="button" class="btn btn-primary" id="confirmed">ok</button>
</div>
</div>
</div>
</div>
<!-- end modal -->
<span class="glyphicon glyphicon-trash remove_comment_btn" id="remove_comment_btn_1790"
data-toggle="modal" data-target="#action_modal" data-href="/do_delete/" data-row="1790"
data-action="approve_remove"
data-modal_title="Agreement"
data-modal_body="are you sure you want to delete?"
aria-selected="false" aria-hidden="true"> Delete 1790</span>
<br />
<span class="glyphicon glyphicon-trash remove_comment_btn" id="remove_comment_btn_1791"
data-toggle="modal" data-target="#action_modal" data-href="/do_delete/" data-row="1791"
data-action="approve_remove"
data-modal_title="Agreement"
data-modal_body="are you sure you want to delete?"
aria-selected="false" aria-hidden="true"> Delete 1791</span>
Expectation
My problem is - I need it to be reset each time cancel is clicked! Any ideas?
UPDATE
It seams to affect ALL modals. As soon as you open one of the similar modals, it will start piling data up and affect all you previously opened.
It's been a while up and no-one has solution. Luckily, I found one. And it's fairly simple. Problem is action binding for click: each time the modal is opened, new event for click is built, but never will never go off until action/event is completed or page refresh. So solution is simple - add off for click before on! No point to copy ALL the code from above, so just the trouble part!
...
case "approve_remove":
const elem_ = button_.parent().parent();
modal_.find(".modal-body").html(`${question_}`);
$("#confirmed").off("click");
$("#confirmed").on("click", function(e) {
...
And that will fix all double, triple, etc. events piling up on modal cancel/close!

I have a 100 button, click each button to display its corresponding bomb box, With javascript and angular

a button corresponding to a prompt box,each box is different shells;Although implements the desired function, but my code is too complicated, and that there is no simple way. how can I do? This is my code
<--html button-->
button1
button2
...
button100
<--html pop box-->
<div class="note1" style="display:none;">
<img class="title-css" src="note1.png">
<p class="one">note1</p>
</div>
...
<div class="note100" style="display:none;">
<img class="title-css" src="note100.png">
<p class="one">note100</p>
</div>
<--angular js-->
$scope.showRulePop = function(index) {
for(var i=1;i<=8;i++) {
$('.note'+i).hide();
}
$('.note'+index).show();
};
Well first of all, don't use jQuery, unless your in the directive level of angular jQuery have nothing to do there.
First let's get rid of the links part using a simple ng-repeat :
<--html button-->
<div ng-repeat="button in buttons">
{{button.label[i]}}
</div>
// JS in the controller
$scope.buttons = [{
label:'button1'
},{label:'button2'}];
As you can see i declare in the javascript all your buttons and i just loop over it.
Now the "bombox" or whatever it is let's make it a simple template :
<div class="{{currentnote.class}}" ng-if="currentNote">
<img class="title-css" src="{{currentNote.img}}">
<p class="one">{{currentNote.content}}</p>
</div>
// and use ng-repeat for the eight first when there is no button selected
<!-- show 1 to 8 if note current note selected -->
<div ng-repeat="button in buttons1To8" ng-if="!currentNote">
<div class="{{button.note.class}}">
<img class="title-css" src="{{button.note.img}}">
<p class="one">{{button.note.content}}</p>
</div>
</div>
// JS
$scope.buttons = [{
label:'button1'
note:{class:'note1', img:'note1.png', content:'note1'//assuming no HTML or you' ll need something more
}},{label:'button2', note:{...}}, ...];
$scope.showRulePop = function(index){
$scope.currentNote = $scope.buttons[index].note;
}
$scope.buttons1To8 = $scope.buttons.slice(0, 8);//0 to 7 in fact
That's all, no need of jQuery.

in meteor how to stop functionalities to work at a time?

In meteor, I have created a cards dynamically after submitting the form. and the dynamic card contains the show and hide buttons. When I click on show option button the hidden part is showing for all the cards instead of that particular card. the problem is the card is creating dynamically so I thought that is problem .. how to make the functionality to work separately to the each card.
Here I am attaching the code:
<div id="newActionCard">
{{#each newaction}}
<div class="workflowcard">
<div class="module-card">
<div class="res-border"></div>
<div class="card-img"></div>
<div class="res-content">
<div class=" assigned-team">{{team}}</div>
<div class=" newaction-name">{{action_title}}</div><hr>
<div class="description">{{description}}</div>
<div class=" due-on">Due on:{{d_date}}</div><hr>
<div class="subcontent">
{{> actioncardsubcontent}}
</div>
<div class="reqext">
{{> requestextensioncard}}
</div>
</div>
<div class="due">
Due on:
<div>
<span class="day-stamp">THU</span><br>
<div class="date-stamp">26</div>
<div class="month-stamp">AUG
</div>
</div>
</div>
</div>
<div class="btn-box newaction">
<button type="button" class="cancelsub" >New Action</button>
<button type="submit" class="createbtnsub" >Show Options</button>
</div>
<div class="btn-box showoption">
<button type="button" class="hideoption" style="display:none">Hide Options</button>
</div>
{{/each}}
</div>
In JS I have written the hide and show functionalities in the events..now how to stop functionality to all cards at a time.
Here is my JS:
Template.workflow.events({
"click .createbtnsub" : function() {
$( ".subcontent" ).show();
$('.createbtnsub').hide();
$('.cancelsub').hide();
$('.hideoption').show();
$('.requestextension').show();
},
"click .hideoption": function(){
$('.subcontent').hide();
},
"click .hideoption": function(){
$(".subcontent").hide();
$('.cancelsub').show();
$('.createbtnsub').show();
$('.requestextension').hide();
$('.hideoption').hide();
$('.reqext').hide();
},
"click #createActionBtn": function(){
$('#createAction').hide();
$('.editw').show();
$('.hidew').show();
},
});
Template.actioncardsubcontent.rendered = function(){
this.$(".subcontent").hide();
};
Template.requestextensioncard.rendered = function(){
this.$(".reqext").hide();
};
Template.workflow.helpers({
getWorkflow: function(){
return Workflow.find();
},
user: function(){
return Meteor.users.find({});
},
getNewaction: function(){
return Newaction.find();
},
});
Please see the following and adjust the selectors as needed:
"click .showoption": function(event){
$(event.currentTarget).next('button').show();
}
This will work for selecting sibling elements, however as a tip I would recommend rewriting your template.helper that returns the cards data from the database into something more specific.
Something like dynamic classes based on index or id so your class/id names would return as follows .showoption-12kjddfse4 . Then you can just get the attribute of the current target and apply it to your js selector.
Also as kind of an explination to why all buttons were showing, is you were using the class selector, which is meant for grabbing groups of elements/nodes. This is also another reason to created more specific classnames/ids to your use case.
So in your class name you could do something like
<button class="showoption" id="{{_id}}">button</button>
<button id="hiddenoption-{{_id}}" class="hiddenoption">button</button>
Then selecting your elements would be easier as follows:
'click .showoption'(event) => {
let id = event.currentTarget.getAttribute(id);
document.getElementById('hiddenoption-'+id).style.display = 'block';
}

Is there a better way to dynamically load a viewmodel and view to display a modal than binding the name of the module on app.js?

What I'm doing feels wonky to me, and I would love to know if there is a better way to do this: I'm using
<compose view-model.bind="modalModel.viewModel"></compose>
in my app.html. When I need to display a modal, whatever viewmodel is active will fire off an event-aggregator publish event with a modal model which contains three things. Modal title, an array of buttons (strings), and a module path to a view/viewmodel. In app.js, I'm subscribing to the modal event and when one comes through, I set the app's
this.modalModel = modalModel;
which immediately binds and loads the view and viewmodel, then I display the modal (bootstrap modal). This works, but there are a few problems which tells me that there must be a better way to do it:
canDeactivate() and deactivate() are never called on the modal's viewmodel that is loaded, when the app.js's modalModel is reset.
I don't think I can pass any parameters into the activate() method in the modal viewmodel.
It just feels wonky.
Would there happen to be a better way to dynamically load in a view and viewmodel, bound and everything, in such a way that it can be properly deactivated upon closing the modal?
Relevant Code:
<template>
<require from="./app-head"></require>
<require from="./main-nav"></require>
<!-- actual site content removed for this example -->
<div class="modal" id="main-modal">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-ajax"></div>
<div class="modal-header">
<button type="button" class="close" click.delegate="modalButtonClick('Cancel')">×</button>
<h4 class="modal-title">${modalModel.title}</h4>
</div>
<div class="modal-body">
<!-- EMPHASIS HERE: -->
<compose view-model.bind="modalModel.viewModel"></compose>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-sm btn-default" repeat.for="button of modalModel.buttons"
click.delegate="modalButtonClick(button)">
${button}
</button>
</div>
</div>
</div>
</div>
</template>
export class App {
//...
attached() {
//...
this.listen('show-modal', this.displayModal.bind(this));
this.listen('hide-modal', this.closeModal.bind(this));
this.listen('show-modal-loader', this.showModalLoader.bind(this));
this.listen('hide-modal-loader', this.hideModalLoader.bind(this));
}
displayModal(modalModel) {
this.modalModel = modalModel;
$('#main-modal').modal();
}
closeModal() {
$('#main-modal').modal('hide');
this.modalModel = null;
}
showModalLoader() {
document.querySelector('#main-modal .modal-ajax').style.display = 'block';
}
hideModalLoader() {
document.querySelector('#main-modal .modal-ajax').style.display = 'none';
}
listen(event, fn) {
this.eventAggregatorSubscriptions.push(this.ea.subscribe(event, fn));
}
}
Use the aurelia-dialog plugin.
To install: jspm install aurelia-dialog
To activate: in main.js configure(), call aurelia.use.plugin('aurelia-dialog');
Usage: https://github.com/aurelia/dialog#using-the-plugin

Bootstrap Tour won't load on a Modal

I have implemented bootstrap tour on my project. I have tried to load it on a modal window but nothing is displayed. Below is my code :
<script type="text/javascript">
$(document).ready(function () {
// Instance the tour
var tour = new Tour({
steps: [
{
element: "#facility_name_div",
title: "Select Facility",
content: "Change the Displayable Facility. "
}
]});
// Initialize the tour
tour.init();
// Start the tour
tour.start();
});
</script>
And my modal window is below:
<!-- Modal -->
<div class="modal fade" id="myModal" role="dialog">
<div class="modal-dialog">
<!-- Modal content-->
<div class="modal-content">
<form class="query_other_order_no_form" id="query_other_order_no_form">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">×</button>
<h4 class="modal-title">Select Facility : </h4>
</div>
<div class="modal-body">
<div class="box-body">
<div class="row">
<div class="col-md-6">
<div class="form-group">
<div id="facility_name_div"></div>
<label>Please Select a Facility : </label>
<select class="form-control select2 c_bpartner_name" id="c_bpartner_name" required="" style="width: 100%;">
<option selected="selected" value="">Please Select One : </option>
</select>
</div>
<!-- /.form-group -->
</div>
<!-- /.col -->
</div>
<!-- /.row -->
</div>
<!-- /.box-body -->
</div>
<div class="modal-footer">
<!--<button type="button" id="bpartner_query_btn" class="btn btn-default bpartner_query_btn pull-left">Query</button> <button type="button" class="btn btn-danger" data-dismiss="modal">Close</button>-->
</div>
</form>
</div>
</div>
</div>
Please can you advise on how I can set up the bootstrap tour appear on the modal window?
I suspect you may be running into an issue of your modal not being visible when the step is firing. However I would need more context to make sure. One way you can check is to use one of the really helpful bits of Bootstrap Tour which is the debug mode. This can be added to your tour object like this:
var tour = new Tour({
debug: true,
steps: [
{
element: "#facility_name_div",
title: "Select Facility",
content: "Change the Displayable Facility. "
}
]});
By adding this line, your tour will print out what it's doing in the browser console. The tour will print out that it's skipping a step because the element isn't visible which is super helpful.
I recently ran into this visibility issue and this is a workaround (admittedly not the greatest) that I added to the onNext function of the previous step in my tour. I am totally open to a better workaround for this:
onNext: function(tour){
tour.end();
setTimeout(function(){
tour.restart();
tour.goTo(4); //substitute your step index here
}, 500); //change this to as little as your load time allows
}
Another thing to be mindful of is your z-index of both your modals and the tour which utilizes Bootstrap's popover functions. If the modal is visible and your debug mode shows that the step is currently shown, you may have to adjust the z-indexes of one or the other to make sure that the modal isn't hiding the popover.
A simple solution is to init and start your tour only once the modal is shown...
With your code this would give :
<script type="text/javascript">
$(document).ready(function () {
// Instance the tour
var tour = new Tour({
steps: [
{
element: "#facility_name_div",
title: "Select Facility",
content: "Change the Displayable Facility. "
}
]});
$('#myModal').on('shown.bs.modal', function (e) {
// Initialize the tour
tour.init();
// Start the tour
tour.start();
});
});
</script>
In the bootstrap 3 and Bootstrap Tour 0.11 there is a CSS conflict with fade class (put the opacity on 0 "zero") . Try this CSS:
.tour-tour.fade.in{
opacity: 1;
}
You need to set Popever's index. The tour will be visible when it does.
Example:
.popover[class*=tour-]{
z-index:100503 !important;
}

Categories

Resources