Jquery mobile chained popup dynamic positioning - javascript

I have a jquery-mobile popup #graphic-menu activated by an onClick event (getPosition() is in another included script, and works as expected):
HTML
Graphic menu
CSS
function showGM(pos){
$("#menu-buttons").popup("close");
$("#menu-buttons").bind({
popupafterclose: function(event, ui){
$("#graphic-menu").popup("open", {x:pos.x, y:pos.y});
}
});
}
The catch is that #gm-btn is itself part of a parent popup #menu-buttons that:
is called to appear several times
appears at different locations (it's a dragable element)
showGM(pos) uses the position of the #gm-btn when the parent popup first appears, and doesn't change for subsequent child popups when reclicked.
How can I get showGM(getPosition(this)) to correctly recalculate the position of #gm-btn when called (or, more likely, what have I screwed up in my code)?
Edit: at the moment, I'm making things work by storing the position in a variable with global scope (since there's only ever one #menu-buttons active), and passing that to #graphic-menu so it knows where to open. I think this probably isn't good programming practice, so if someone has a better suggestion I'd love to hear about it.

Based on the API (http://api.jquerymobile.com/popup/), they say:
Note: Chaining of popups not allowed: "The framework does not currently support chaining of popups so it's not possible to embed a
link from one popup to another popup. All links with a
data-rel="popup" inside a popup will not do anything at all."
A workaround to get chained popups working is the use of a timeout for
example in the popupafterclose event bound to the invoking popup. In
the following example, when the first popup is closed, the second will
be opened by a delayed call to the open method:
$( document ).on( "pageinit", function() {
$( ".popupParent" ).on({
popupafterclose: function() {
setTimeout(function() { $( ".popupChild" ).popup( "open" ) }, 100 );
}
});
});
In regards to jsFiddle, it works good for this type of testing: https://jsfiddle.net/Twisty/4gjdp4te/7/ You had not set the JQuery library, so I set it to 2.1.3, and retained your JQM 1.4.5 link.
My attempt so far has not yielded the results you want, but you can at least see what direction to take. I plan to keep working on it and will update my answer.
HTML
<div data-role="page">
<div role="main" class="ui-content">
<a id="anchor1" href="#pop-1" data-rel="popup" data-transition="pop">Basic Popup</a>
<div data-role="popup" id="pop-1" data-position-to="#anchor1">
<p>This is a basic popup.</p>
<button id="firstbutton" data-rel="popup">Button to open 2nd popup</button>
</div>
<div data-role="popup" id="pop-2">
<p>This is another basic popup.</p>
</div>
</div>
</div>
JQUERY
var buttonClick = false;
$(document).on("pageinit", function () {
$("#pop-1").on({
popupafterclose: function () {
if (buttonClick) {
console.log("Opening 2nd Popup.");
setTimeout(function () {
$("#pop-2").popup("open", {
positionTo: "#pop-1"
})
}, 100);
buttonClick = false;
} else {
console.log(buttonClick);
}
}
});
});
$(document).ready(function () {
$("#firstbutton").click(function () {
buttonClick = true;
console.log("First Button clicked");
$("#pop-1").popup("close");
});
});
EDIT
I got the code you need: https://jsfiddle.net/Twisty/4gjdp4te/8/
JQUERY
var buttonClick = false;
$(document).ready(function () {
$("#pop-1").popup({
afterclose: function(){
if (buttonClick) {
console.log("Opening 2nd Popup.");
setTimeout(function () {
$("#pop-2").popup("open", {
positionTo: "#pop-1"
})
}, 100);
buttonClick = false;
} else {
console.log(buttonClick);
}
}
});
$("#firstbutton").click(function () {
buttonClick = true;
console.log("First Button clicked");
$("#pop-1").popup("close");
});
});

Related

Problems with automating the function jquery

Hello guys Here i have a function that reacts two time by a same button. One time when it's clicked it fades out other time it fades in, but the problem is one that after 2 clicks it stops responding. I am trying to make it loop but. I don't have any clue. I tried with the clickCounts ++ and if statements but it didn't give me any fruit.
so if you guys have any idea I'm quite opened to any suggestions.
$(function() {
$('#two').one("click", function() {
$("this").css({color:"#f790e8"})
$(".others:nth-child(1)").fadeOut("300")
$(".others:nth-child(2)").delay("150").fadeOut("300")
$(".others:nth-child(3)").delay("300").fadeOut("300")
$(".others:nth-child(4)").delay("450").fadeOut("300")
$(".tube1").delay("300").fadeIn("300")
$(".tube2").delay("450").fadeIn("300")
$(".tube3").delay("600").fadeIn("300")
$('#two').on("click", function() {
//this code will execute on second click and further clicks
$("this").css({color:"black"})
$(".others:nth-child(1)").delay("300").fadeIn("300")
$(".others:nth-child(2)").delay("450").fadeIn("300")
$(".others:nth-child(3)").delay("600").fadeIn("300")
$(".others:nth-child(4)").delay("750").fadeIn("300")
$(".tube1").fadeOut("300")
$(".tube2").delay("150").fadeOut("300")
$(".tube3").delay("300").fadeOut("300")
});
});
});
You'll probably have a better time setting a class on the element and using it to see which one of the two behaviors to trigger.
$(function () {
$("#two").on("click", function () {
const $this = $(this);
if ($this.hasClass("on")) {
//this code will execute on second click and further clicks
$this.css({ color: "black" });
$(".others:nth-child(1)").delay("300").fadeIn("300");
$(".others:nth-child(2)").delay("450").fadeIn("300");
$(".others:nth-child(3)").delay("600").fadeIn("300");
$(".others:nth-child(4)").delay("750").fadeIn("300");
$(".tube1").fadeOut("300");
$(".tube2").delay("150").fadeOut("300");
$(".tube3").delay("300").fadeOut("300");
} else {
$this.css({ color: "#f790e8" });
$(".others:nth-child(1)").fadeOut("300");
$(".others:nth-child(2)").delay("150").fadeOut("300");
$(".others:nth-child(3)").delay("300").fadeOut("300");
$(".others:nth-child(4)").delay("450").fadeOut("300");
$(".tube1").delay("300").fadeIn("300");
$(".tube2").delay("450").fadeIn("300");
$(".tube3").delay("600").fadeIn("300");
}
$this.toggleClass("on");
});
});

JavaScript - close popup with back button

I have a custom popup functionality. What I want is for the browser back button to close this popup.
My ideal scenario would be to NOT show a hashtag in the URL bar.
I have tried putting window.history.pushState('forward', null, ''); in my showPopup() function and then doing the following:
$(window).on('popstate', function () {
closePopup();
});
This does work but the problem is when I manually close the popup I have to press the back button twice to navigate back to the previous page (obviously because a browser history entry was added when the popup was opened).
What is the best way of doing this? Can it be done without adding a browser history entry? Essentially what I am trying to do is replicate the behaviour of a mobile app. Press the back button in a mobile app will usually dismiss any open modals or context menus.
$('.popup-link').click(function() {
showPopup();
});
$('.popup-close').click(function() {
hidePopup();
});
function showPopup() {
$('.popup').addClass('active');
}
function hidePopup() {
$('.popup').removeClass('active');
}
.popup {
background-color: #ccc;
width: 300px;
height: 300px;
display: none;
}
.popup.active {
display: block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<button class="popup-link">Click</button>
<div class="popup">
<button class="popup-close">x</button>
<!-- popup content here -->
</div>
It is not possible to do it without adding browser history entries since you cannot override the back button behaviour, see Intercepting call to the back button in my AJAX application: I don't want it to do anything
Sujumayas answer is a good option, you should introduce some additional variable though to avoid problems with the history when opening multiple popups (e.g. when clicking the button multiple times)
Here is some possible sample code:
let popupOpen = false;
$(".popup-link").click(function() {
showPopup();
});
$(".popup-close").click(function() {
window.history.back();
});
function showPopup() {
if (popupOpen) {
window.history.back();
}
popupOpen = true;
window.history.pushState("forward", null, "");
$(".popup").addClass("active");
}
function hidePopup() {
popupOpen = false;
$(".popup").removeClass("active");
}
$(window).on("popstate", function() {
hidePopup();
});
Additionally please note that you might have problems with Opera Mini: https://caniuse.com/#search=history
Altho I don't recommend to override regular browser history managment (back button) to use it as you please....
I think that the only thing you missed in your example is that the close button should not close the modal by itself, but instead just execute a backbutton event (which will eventually close the modal).
That simple fix and it will work as you wanted.
I am doing already something like this, and it works nicely with the browser back-button and by pushing the android back-button as well. I am also not showing a hashtag in the URL bar.
Here is the stub (I just tried to apply that to Your scenario):
function freezeHistory() {
window.history.pushState({}, window.document.title, window.location.href);
}
function goBack() {
/*
Custom history back actions: close panel, close popup, close drop-down menu
*/
var popupOpen = $(".popup.active").length > 0;
if(popupOpen) {
hidePopup();
return false;
}
window.history.back();
return true;
}
function showPopup() {
$('.popup').addClass('active');
freezeHistory();
}
function hidePopup() {
$('.popup').removeClass('active');
}
$(window).on("popstate", function(e) {
/*
Browsers tend to handle the popstate event differently on page load.
Chrome (prior to v34) and Safari always emit a popstate event on page load,
but Firefox doesn’t.
*/
goBack();
})
If this won't work for You out-of-the box, it is because IMHO You may need to clarify a little bit how do You expect to manage the page history. Feel free to add more detail to Your question if this isn't working as You'd expect now, but anyway, I strongly believe You got the idea and You are able to apply it inside the scenario of Your web-app.
Open popup and try going back and forth with the browser history buttons
$(document).ready(function () {
// manage popup state
var poped = false;
$('.popup-link').click(function () {
// prevent unwanted state changtes
if(!poped){
showPopup();
}
});
$('.popup-close').click(function () {
// prevent unwanted state changtes
if(poped){
hidePopup();
}
});
function showPopup() {
poped = true;
$('.popup').addClass('active');
// push a new state. Also note that this does not trigger onpopstate
window.history.pushState({'poped': poped}, null, '');
}
function hidePopup() {
poped = false;
// go back to previous state. Also note that this does not trigger onpopstate
history.back();
$('.popup').removeClass('active');
}
});
// triggers when browser history is changed via browser
window.onpopstate = function(event) {
// show/hide popup based on poped state
if(event.state && event.state.poped){
$('.popup').addClass('active');
} else {
$('.popup').removeClass('active');
}
};
.popup {
background-color: #ccc;
width: 300px;
height: 300px;
display: none;
}
.popup.active {
display: block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<button class="popup-link">Click</button>
<div class="popup">
<button class="popup-close">x</button>
<!-- popup content here -->
</div>
You could add window.history.go(-2) to your popstate. That should take you back twice, which would be your original page before the modal as pushState added an entry to your history object.
Conversely, you could use history.back(2)
Use window.location.href to go 2 pages back and reload
Just run window.history.back(); when closing the popup.
$('.popup-close').click(function() {
hidePopup();
window.history.back();
});
You would have two options to implement this:
Option 1: Using the window.beforeunload event. reference
$('.popup-link').click(function() {
showPopup();
$(window).on("beforeunload", hidePopup);
});
$('.popup-close').click(hidePopup);
function hidePopup() {
$(window).off("beforeunload", hidePopup);
$('.popup').removeClass('active');
}
Demo
Option 2: Using the HTML5 History API. reference
$('.popup-link').click(function() {
showPopup();
window.history.pushState('popup-open', null, '');
$(window).on('popstate', hidePopup);
});
$('.popup-close').click(function() {
if(history.state == 'popup-open') {
window.history.back();
}
hidePopup();
});
function hidePopup() {
$(window).off('popstate', hidePopup);
$('.popup').removeClass('active');
}
Demo
Edit: sujumayas's idea is also pretty good one.
Demo
Further, I'ld recommend to register the popstate / beforeunload events only when necessary and unregister them, when you no longer need 'em in order to avoid overhead.
if anyone use bootstrap with any version you can use this
let popupOpen = false;
//show
$(".popup-link").on('click',(function() {
showPopup();
})
);
// hide
$(".popup-close").on('click',(function() {
window.history.back();
})
);
// on click back button
$(window).on("popstate", function() {
if (popupOpen) {
hidePopup();
}
});
// for bootstrap, if clicked outside the modal or close somehow
$(window).on('hidden.bs.modal', function(e) {
// Make sure is open and the same modal
if (e.target.id=='exampleModal' && popupOpen) {
popupOpen = false;
window.history.back();
}
});
function showPopup() {
// if open back
if (popupOpen) {
window.history.back();
}
popupOpen = true;
// push the browser history
window.history.pushState("forward", null, "#popup");
// you have to use the id
$("#exampleModal").modal('show');
}
function hidePopup() {
popupOpen = false
// you have to use the id to close the modal
$("#exampleModal").modal('hide');
}

Jquery function doesn't work after Ajax call

I've got this function:
$(document).ready(function() {
$('.post_button, .btn_favorite').click(function() {
//Fade in the Popup
$('.login_modal_message').fadeIn(500);
// Add the mask to body
$('body').append('<div class="overlay"></div>');
$('.overlay').fadeIn(300);
return false;
});
My page loads content with favourite buttons, but after Ajax call and generated additional new content the function doesn't work when you click new content's buttons. What could be not right?
That is because you are using dynamic content.
You need to change your click call to a delegated method like on
$('.post_button, .btn_favorite').on('click', function() {
or
$("body").on( "click", ".post_button, .btn_favorite", function( event ) {
Instead of this:
$('.post_button, .btn_favorite').click(function() {
do this:
$(document).on('click','.post_button, .btn_favorite', function() {
on will work with present elements and future ones that match the selector.
Cheers
class-of-element is the applied class of element. which is selector here.
$(document).on("click", ".class-of-element", function (){
alert("Success");
});
If you know the container for .post_button, .btn_favorite then use
$('#container_id').on('click', '.post_button, .btn_favorite', function () { });
so if '.post_button, .btn_favorite' are not found then it will bubble up to container_id
else if you don't know the container then delegate it to document
$(document).on('click', '.post_button, .btn_favorite', function () { });
Reference
I am not sure if I am getting your question right but you may want to try..
$.ajax({
url: "test.html"
}).done(function() {
$('.post_button, .btn_favorite').click(function() {
//Fade in the Popup
$('.login_modal_message').fadeIn(500);
// Add the mask to body
$('body').append('<div class="overlay"></div>');
$('.overlay').fadeIn(300);
return false;
});
Just try to paste your code inside done function.
Hope it helps :)
EDIT:
I also notice you are missing }); on your question.
The following worked for me
$(document).ready(function(){
$(document).bind('contextmenu', function(e) {
if( e.button == 2 && jQuery(e.target).is('img')) {
alert('These photos are copyrighted by the owner. \nAll rights reserved. \nUnauthorized use prohibited.');
return false;
}
});
});
You need to bind the jQuery click event once your ajax content is replaced old content
in AJAX success block you need to add code like here new response html content one a tag like
Click Me
So you can bind the new click event after change the content with following code
$("#new-tag").click(function(){
alert("hi");
return false;
});

Jasmine doesn't seem to be calling my method

I am new to Jasmine and seem to be struggling to get what I think is a fairy standard kind of thing running.
I am loading an HTML file via a fixture and trying to call a click on an element on the dom. This I would expect result in the call to the method of the JS file I am trying to test. When I try and debug this in developer tools the method that should be called in my js file never hits a breakpoint. As such I assume that code is not being called and therfore does not toggle the expand/collapse class.
My test:
describe("userExpand", function () {
beforeEach(function () {
loadFixtures('user-expand.html');
//userControl();
//this.addMatchers({
// toHaveClass: function (className) {
// return this.actual.hasClass(className);
// }
//});
});
//this test works ok
it("checks the click is firing", function () {
spyOnEvent($('.expanded'), 'click');
$('.expanded').trigger('click');
expect("click").toHaveBeenTriggeredOn($('.expanded'));
});
//this doesn't
it("checks the click is changing the class", function () {
//spyOnEvent($('.collapsed'), 'click');
var myElement = $('.collapsed');
myElement.click();
expect(myElement).toHaveClass('.expanded');
});
Part of the fixture:
<div class="wrapper">
<div class="row group">
<div class="col-md-1" data-bordercolour=""> </div>
<div class="collapsed col-md-1"> </div>
<div class="col-md-9">None (1)</div>
The JS I am trying to test:
var userControl = function () {
"use strict";
var collapse = '.collapsed';
var expand = '.expanded';
var userList = $(".userList");
function toggleState() {
var currentControl = $(this);
if (currentControl.hasClass('all')) {
if (currentControl.hasClass('expanded')) {
toggleIcon(currentControl, collapse);
userList.find(".user-group-summary").hide()
.end()
.find(".user-group-info").show();
} else {
toggleIcon(currentControl, expand);
userList.find(".user-group-summary").show()
.end()
.find(".user-group-info").hide();
}
} else {
currentControl.parent().nextUntil('.group').toggle();
currentControl.toggleClass("expanded collapsed");
currentControl.parent().find(".user-group-summary").toggle()
.end()
.find(".user-group-info").toggle();
}
};
function toggleIcon(ctrl, currentState) {
var details = ctrl.closest('div.row').siblings('.wrapper');
details.find(currentState).toggleClass('expanded collapsed');
if (currentState === expand) {
details.find('.detail').hide();
} else {
details.find('.detail').show();
}
}
userList.on('click', '.expanded, .collapsed', toggleState);
$('[data-bordercolour]').each(function () {
$(this).css("background-color", $(this).data('bordercolour'))
.parent().nextUntil('.group')
.find('>:first-child').css("background-color", $(this).data('bordercolour'));
});
return {
toggleState: toggleState
};
}();
The code works fine in normal use so I am sure I am missing something obvious with the way Jasmine should be used. Any help would be appreciated.
Update:
I can make the togglestate method fire by using call in the test rather than triggering a click event:
it('checks on click of icon toggles that icon', function () {
var myElement = $('.collapsed');
userControl.toggleState.call(myElement);
expect(myElement).toHaveClass('expanded');
});
This seems a little strange as all the examples I can find are quite happy with click. Gets me off the hook but I would still like to know what I am missing.
It's hard to give a precise hint without the source code. Does click on .collapsed involve asynchronous action(s)? If so, wrapping the test in runs(...); waitsFor(...); runs(...); may solve the problem. Check the Jasmine introduction for how to do this.

prevent function from running twice

I have a slideshow which runs automatically and you can skip to an image by clicking on a button.
It works fine if you click one of the buttons when the image is static, but if you click while the fade functions are running it will run the functions twice which creates some kind of loop which eventually grinds the browser to a stand still!
I know I need to add some kind of "isRunning" flag, but I don't know where.
Here's a link to a jsfiddle - http://jsfiddle.net/N6F55/8/
And code also below...
javascript:
$(document).ready(function() {
var images=new Array();
var locationToRevealCount=6;
var nextimage=2;
var t;
var doubleclick;
addIcons();
function addIcons() {
while (locationToRevealCount>0) {
$("#extraImageButtons").append('<img class="galleryButtons" src="http://www.steveszone.co.uk/images/button_sets/pink_square_button1n.png" alt="'+locationToRevealCount+'" />');
images[locationToRevealCount]='http://www.tombrennand.net/'+locationToRevealCount+'a.jpg';
locationToRevealCount--;
};
$('.homeLeadContent').prepend('<img class="backgroundImage" src="http://www.tombrennand.net/1a.jpg" />');
$("#extraImageButtons img.galleryButtons[alt*='1']").attr("src","http://www.steveszone.co.uk/images/button_sets/black_square_button1n.png");
runSlides();
}
function runSlides() {
clearTimeout(t);
t = setTimeout(doSlideshow,3000);
}
function doSlideshow() {
if($('.backgroundImage').length!=0)
$('.backgroundImage').fadeOut(500,function() {
$('.backgroundImage').remove();
slideshowFadeIn();
});
else
slideshowFadeIn();
}
function slideshowFadeIn() {
if(nextimage>=images.length)
nextimage=1;
$("#extraImageButtons img.galleryButtons").attr("src","http://www.steveszone.co.uk/images/button_sets/pink_square_button1n.png");
$("#extraImageButtons img.galleryButtons[alt*='"+nextimage+"']").attr("src","http://www.steveszone.co.uk/images/button_sets/black_square_button1n.png");
$('.homeLeadContent').prepend($('<img class="backgroundImage" src="'+images[nextimage]+'" style="display:none;">').fadeIn(500,function() {
nextimage++;
runSlides();
}));
}
$("#extraImageButtons img.galleryButtons").live('click', function() {
nextimage=$(this).attr("alt");
$("#extraImageButtons img.galleryButtons").attr("src","http://www.steveszone.co.uk/images/button_sets/pink_square_button1n.png");
$(this).attr("src","http://www.steveszone.co.uk/images/button_sets/black_square_button1n.png");
clearTimeout(t);
doSlideshow();
});
});
html:
<div class="homeLeadContent" style="width:965px;">
</div>
<div id="extraImageButtons"></div>
Two changes make it work better for me:
Down in the "extra image buttons" handler, you call "clearInterval()" but that should be changed to "clearTimeout()".
I added another call to "clearTimeout(t)" in the "runSlides()" function right before it sets up another timeout.
Clicking on the big "CLICK ME" button might still do weird things.
edit — well here is my fork of the original jsfiddle and I think it's doing the right thing. In addition to calling "clearTimeout()" properly, I also changed the code in "doSlideshow()" so that it empties out the content <div> before adding another image.

Categories

Resources