Pass JS variable across mouseenter event from jquery in angular2 app - javascript

I have trouble witch function below:
inactive() {
let target = $('.album__item--cover-container');
var leave:boolean;
target.mouseleave(function() {
leave = true;
console.log("LEAVE MOUSEENTER: ", leave);
});
console.log("LEAVE: ", leave);
if (leave) {
console.log("LEAVE CONDITION: ", leave);
this.flyOut = 'inactive';
}
console.log(this.flyOut);
}
I don't know wht log "LEAVE MOUSEENTER" is true, and after exit from mouse event log "LEAVE" is undefined. I can't declate this.flyOut = true in this event function. Please someone helop me, and explain why this behaviour goes that way?
Regards
Greg

I think your issue is because you are not doing anything inside the callback of the mouse leave event.
This is only run once when inactive() is invoked.
if (leave) {
console.log("LEAVE CONDITION: ", leave);
this.flyOut = 'inactive';
}
I'm assuming you are trying to set some state on mouseleave, and you then want to check if this.flyOut is inactive?
(function ($) {// just some wrapper
let flyOut = 'inactive'
const target = $('.album__item--cover-container'); // Use const if you know it wont change
const check = $('.check-button-class-thing');
target.mouseleave(function() {
flyOut = 'inactive'
});
target.mouseenter(function() {
flyOut = 'active'
});
check.click(function() {
console.log(inactive()); // Is it inactive or not
});
function inactive() {
return flyOut === 'inactive' ? true : false;
}
})(jQuery);
I'm making assumptions here on your use case but I hope this helps. The code itself is not very useful as it depends on where the .check-button-class-thing is nested but its just an example.

The
if (leave) {
is out of the mouseleave function callback. So when inactive() is called the first time, leave has been declared without any default value (it's undefined). And the if is called.
It will only be true when the mouseleave event is called.

Related

Using callbacks JS / JQuery

I am trying to use callbacks in order to effectively "overwrite" the standard alert and confirm actions in JavaScript.
The code I am using is a bit long winded so I jotted it into a working jsfiddle
I am trying to get it so that a callback is used to determine true or false, but it is coming back as undefined as the callback function is fired before a click is
My questions, is how can I change this to effectively overcome the functions value being returned before the click is called via jQuery?
Example usage:
<button onclick="confirm('This is a test')">Show dialog (confirm)</button>
Example jQuery events:
if (confirm("This is a test")) {
alert("Confirmed")
}
else {
alert("Did not confirm")
}
Edit
Using a loop within the callback messed it us a lot...
You are mixing things up when waiting for the return value.
You are passing dialog.checkForInput as the callback. But in the dialog.show() function you do:
var ret = callback();
...
return ret;
But the dialog.checkForInput function doesn't return anything, it merely sets event listeners.
As events all run "asynchronously" it would be more sensible to give your dialog a callback function which will be run when there actually would be an event. Meaning: in your checkForInput function (I would name it differently, but whatever) run the callback and pass the action as a parameter. Something like:
checkForInput: function () {
$(document).ready(function () {
$(".dialog_confirm_okay").on("click", function () {
dialog.hide();
callback('confirm');
})
$(".dialog_confirm_cancel").on("click", function () {
dialog.hide();
callback('cancel');
})
$(".dialog_alert_okay").on("click", function () {
dialog.hide();
callback('alert');
})
})
}
And your callback could look like this (assuming your callback was called dialogCallback):
function dialogCallback ( action ) {
alert('Dialog closed with action: ' + action);
};
Some points I conclude from your code:
The reason why statement callback() return undefined value is because dialog.checkForInput return nothing.
The $(document).ready inside checkForInput is async, so returned value from that block is meaningless (it won't become the return value of the checkForInput as well).
And also you put the return statement inside event declaration, it'll become return value of the event (when the event triggered), not the checkForInput.
I did some modification on your code, this one working. Basically I create new method called onclick, which will be called every time button yes or no is clicked.
show: function (e_type, e_content) {
var d = dialog;
var d_head = e_type == "confirm" ? "Confirm Action" : "Error";
var d_buttons = e_type = "confirm" ? d.parts.buttons.okay + d.parts.buttons.cancel : d.dparts.buttons.alert_okay;
var _dialog = d.parts.main + d.parts.head.replace("{DIV_HEADER}", d_head) + d.parts.body + e_content + "<div class='dialog_button_container'>" + d_buttons + "</div>" + d.parts.footer;
$("body").append(_dialog);
},
onclick: function (ret) {
$(".errors").text("Return value was: " + ret);
},
showError: function (e_content) {
dialog.show("alert", e_content);
dialog.checkForInput();
},
showConfirm: function (e_content) {
dialog.show("confirm", e_content);
dialog.checkForInput();
},
checkForInput: function () {
var self = this;
$(".dialog_confirm_okay").on("click", function () {
dialog.hide();
self.onclick(true);
})
$(".dialog_confirm_no").on("click", function () {
dialog.hide();
self.onclick(false);
})
$(".dialog_alert_okay").on("click", function () {
dialog.hide();
self.onclick(false);
})
},
Working example: https://jsfiddle.net/p83uLeop/1/
Hope this will help you.
EDITED
From the comment section I assume that you want this alert to become a blocking function like window.confirm, so you can do something like if (confirm('Are you sure')).
But sadly it's impossible to achieve this case.
I have some suggestion, you can encapsulate your code better, and implement clean callbacks or promises. Maybe something like this:
showConfirm(function (ok) {
if (ok) {
// "yes" clicked
} else {
// "no" clicked
}
})
// or
showConfirm(function () {
// "yes" clicked
}, function () {
// "no clicked"
})
// or
var customConfirm = showConfirm()
customConfirm.on('yes', function () {
// "yes" clicked
})
customConfirm.on('no', function () {
// "no" clicked
})

How to execute a function when clicking an area inside an object?

I created an object with map Areas by using DOM-Elements. Now I want to execute a function when clicked on an area(coordinates). I think I also Need to check the area if it is clicked or not ? Also I think I'm missing something on the DOM-Element.
var _images = { //Object-MapArea
start: {
path: 'start.jpg',
areas: [{
coords: '18,131,113,140',
text: 'Homepage',
onclick: doSomething, // ???
}]
},
}
// ..... there is more code not shown here
//Creating DOM-Elements
var areaElem = document.createElement('AREA');
// Set attributes
areaElem.coords = area.coords;
areaElem.setAttribute('link', area.goto);
areaElem.setAttribute("title", area.text);
areaElem.setAttribute("shape", "rect");
areaElem.onclick = doSomething; ? ? ?
if (element.captureEvents) element.captureEvents(Event.CLICK); ? ? ?
areaElem.addEventListener('click', onArea_click);
_map.appendChild(areaElem);
function doSomething(e) {
if (!e) var e = window.event
e.target = ... ? ?
var r = confirm("Data will get lost!");
if (r == true) {
window.open("homepage.html", "_parent");
} else {
window.open(" ", "_parent"); // here I want to stay in the current pictures. I mean the current object map area. But how can I refer to it so it stays there ?
}
}
See the comments I added to your code ;)
var _images = { //Object-MapArea
start: {
path: 'start.jpg',
areas: [{
coords: '18,131,113,140',
text: 'Homepage',
clickHandler: doSomething // I changed the name to avoid confusion but you can keep onclick
}]
},
}
// ..... there is more code not shown here
//Creating DOM-Elements
var areaElem = document.createElement('AREA');
// Set attributes
areaElem.coords = area.coords;
areaElem.setAttribute('link', area.goto);
areaElem.setAttribute("title", area.text);
areaElem.setAttribute("shape", "rect");
// Add a listener on click event (using the function you defined in the area object above)
areaElem.addEventListener('click', area.clickHandler);
// Add your other click handler
areaElem.addEventListener('click', onArea_click);
_map.appendChild(areaElem);
function doSomething(e) {
// Get the area clicked
var areaClicked = e.target;
// I dont understand the following code...
var r = confirm("Data will get lost!");
if (r == true) {
window.open("homepage.html", "_parent");
} else {
window.open(" ", "_parent"); // here I want to stay in the current pictures. I mean the current object map area. But how can I refer to it so it stays there ?
}
}
Hope it helps ;)
I think I also Need to check the area if it is clicked or not
You are creating an element areaElem and attaching event to the same dom.
So there may not be need of any more check
You may not need to add both onclick & addEventListner on same element and for same event. Either onclick function or addEventListener will be sufficient to handle the event.
areaElem.onclick = function(event){
// Rest of the code
// put an alert to validate if this function is responding onclick
}
areaElem.addEventListener('click', onArea_click);
function onArea_click(){
// Rest of the code
}

How to give a div a higher z-index on click with JS?

I asked this question yesterday hopefully this one is clearer as I've now provided a working example of my store.
I'm developing a Shopify Theme. I've been using Timber as my base and I'm currently having a problem with my Quick Cart and Quick Shop/View drawers.
I have 2 drawers on the right of my site, 1 for the cart and 1 for the product quick view option. The drawers currently slide open - #PageContainer moves to the left on click to reveal each drawer.
As they are currently sitting on top of each other I need to alter the JS so that on click the z-index changes so that the correct drawer being called is highest in the stack.
I'm not great with JS so not sure if this is a simple task?
Here is a link to my Dev Store
JS:
timber.Drawers = (function () {
var Drawer = function (id, position, options) {
var defaults = {
close: '.js-drawer-close',
open: '.js-drawer-open-' + position,
openClass: 'js-drawer-open',
dirOpenClass: 'js-drawer-open-' + position
};
this.$nodes = {
parent: $('body, html'),
page: $('#PageContainer'),
moved: $('.is-moved-by-drawer')
};
this.config = $.extend(defaults, options);
this.position = position;
this.$drawer = $('#' + id);
if (!this.$drawer.length) {
return false;
}
this.drawerIsOpen = false;
this.init();
};
Drawer.prototype.init = function () {
$(this.config.open).on('click', $.proxy(this.open, this));
this.$drawer.find(this.config.close).on('click', $.proxy(this.close, this));
};
Drawer.prototype.open = function (evt) {
// Keep track if drawer was opened from a click, or called by another function
var externalCall = false;
// Prevent following href if link is clicked
if (evt) {
evt.preventDefault();
} else {
externalCall = true;
}
// Without this, the drawer opens, the click event bubbles up to $nodes.page
// which closes the drawer.
if (evt && evt.stopPropagation) {
evt.stopPropagation();
// save the source of the click, we'll focus to this on close
this.$activeSource = $(evt.currentTarget);
}
if (this.drawerIsOpen && !externalCall) {
return this.close();
}
// Add is-transitioning class to moved elements on open so drawer can have
// transition for close animation
this.$nodes.moved.addClass('is-transitioning');
this.$drawer.prepareTransition();
this.$nodes.parent.addClass(this.config.openClass + ' ' + this.config.dirOpenClass);
this.drawerIsOpen = true;
// Run function when draw opens if set
if (this.config.onDrawerOpen && typeof(this.config.onDrawerOpen) == 'function') {
if (!externalCall) {
this.config.onDrawerOpen();
}
}
if (this.$activeSource && this.$activeSource.attr('aria-expanded')) {
this.$activeSource.attr('aria-expanded', 'true');
}
// Lock scrolling on mobile
this.$nodes.page.on('touchmove.drawer', function () {
return false;
});
this.$nodes.page.on('click.drawer', $.proxy(function () {
this.close();
return false;
}, this));
};
Drawer.prototype.close = function () {
if (!this.drawerIsOpen) { // don't close a closed drawer
return;
}
// deselect any focused form elements
$(document.activeElement).trigger('blur');
// Ensure closing transition is applied to moved elements, like the nav
this.$nodes.moved.prepareTransition({ disableExisting: true });
this.$drawer.prepareTransition({ disableExisting: true });
this.$nodes.parent.removeClass(this.config.dirOpenClass + ' ' + this.config.openClass);
this.drawerIsOpen = false;
this.$nodes.page.off('.drawer');
};
return Drawer;
})();
Update
As instructed by Ciprian I have placed the following in my JS which is making the #CartDrawer have a higher z-index. I'm now unsure how I adapt this so that it knows which one to have higher dependant on which button is clicked. This is what I've tried:
...
Drawer.prototype.init = function () {
$(this.config.open).on('click', $.proxy(this.open, this));
$('.js-drawer-open-right-two').click(function(){
$(this).data('clicked', true);
});
if($('.js-drawer-open-right-two').data('clicked')) {
//clicked element, do-some-stuff
$('#QuickShopDrawer').css('z-index', '999');
} else {
//run function 2
$('#CartDrawer').css('z-index', '999');
}
this.$drawer.find(this.config.close).on('click', $.proxy(this.close, this));
};
...
The approach would be like this:
$('.yourselector').css('z-index', '999');
Add it (and adapt it to your needs) inside your onclick() function.
if you need to modify the z-index of your div when clicking a buton, you shoud put in this code on your onclick() function, else if you need to activate it when you looding the page you shoud put it on a $( document ).ready() function , the code is :
$('#yourID').css('z-index', '10');
You can use:
document.getElementById("your-element-id").style.zIndex = 5;
It's pure Javascript and sets the z-index to 5. Just bind this to onClick event!

Sinon Spy not working with Javascript call or apply

I'm trying to use sinon.spy to check if the play function is being called for a component. The problem is that the spy counter is not updating, even though I've confirmed that my component's function is indeed being called.
I've tracked it down to the use of Javascript's call function:
handleDone: function(e) {
for (var i = 0; i < this.components.length; i++) {
if (this.components[i].element === e.target) {
if (this.components[i].hasOwnProperty("play")) {
// This won't trigger the spy.
this.components[i]["play"].call(this.components[i].element);
}
break;
}
}
}
A similar thing happens when swapping call for apply.
[UPDATED]
Here is the relevant test:
var page = document.getElementById("page"),
demo = document.getElementById("demo"),
playSpy;
suiteSetup(function() {
playSpy = sinon.spy(demo, "play");
});
suiteTeardown(function() {
demo.play.restore();
});
suite("done", function() {
test("rise-component-done fired from element with play property", function() {
assert(playSpy.notCalled); //true
demo.sendDone();
assert(playSpy.calledOnce); //false
});
});
And the play and sendDone functions in the demo component:
play: function() {
console.log("play");
},
sendDone: function() {
this.dispatchEvent(new CustomEvent("rise-component-done", { "bubbles": true }));
}
The page component is registered to listen for this event:
this.addEventListener("rise-component-done", this.handleDone);
Anyone know of a workaround?
Thx.
So the problem wasn't actually related to the call method at all as Ben illustrated. After much tinkering, I realized I had to change this line:
playSpy = sinon.spy(demo, "play");
to this:
playSpy = sinon.spy(page.components[0], "play");
Now my tests pass. Hooray! I just wish it hadn't taken me the better part of a day to debug.

Multiple click handlers for a single element

I've written a few events to handle opening and closing of a snap js drawer. This code below works, but I feel it could be written more efficiently. Any suggestions?
function openMobileMenu() {
event.preventDefault();
snapper.open('left');
$('#btn-menu').off('click', openMobileMenu);
$('#btn-menu').on('click', closeMobileMenu);
}
function closeMobileMenu() {
event.preventDefault();
snapper.close('left');
$('#btn-menu').on('click', openMobileMenu);
$('#btn-menu').off('click', closeMobileMenu);
}
$('#btn-menu').on('click', openMobileMenu);
Make your code modular and your concepts explicit.
You can start by creating a MobileMenu object which encapsulates the logic.
Note: The following code was not tested.
var MobileMenu = {
_snapper: null,
_$button: null,
_direction: 'left',
init: function (button, snapper, direction) {
this._$button = $(button);
this._snapper = snapper;
if (direction) this._direction = direction;
this._toggleSnapperVisibilityWhenButtonClicked();
},
_toggleSnapperVisibilityWhenbuttonClicked: function () {
this._$button.click($.proxy(this.toggle, this));
},
toggle: function () {
var snapperClosed = this._snapper.state().state == 'closed',
operation = snapperClosed? 'open' : 'closed';
this._snapper[operation](this._direction);
}
};
Then in your page you can just do the following to initialize your feature:
var mobileMenu = Object.create(MobileMenu).init('#btn-menu', snapper);
Modularizing your code will make it more maintainable and understandable in the long run, but also allow you to unit test it. You also gain a lot more flexibily because of the exposed API of your component which allows other code to interact with it.
E.g. you can now toggle the menu visibility with mobileMenu.toggle().
Use a variable to keep track of the state:
var menu_open = false;
$("#btn-menu").on('click', function(event) {
event.preventDefault();
if (menu_open) {
snapper.close('left');
} else {
snapper.open('left');
}
menu_open = !menu_open; // toggle variable
});
snap has a .state() method, which returns an object stuffed with properties, one of which is .state.
I think you want :
$('#btn-menu').on('click', function() {
if(snapper.state().state == "closed") {
snapper.open('left');
} else {
snapper.close('left');
}
});
Or, in one line :
$('#btn-menu').on('click', function() {
snapper[['close','open'][+(snapper.state().state == 'closed')]]('left');
});
Also, check How do I make a toggle button? in the documentation.

Categories

Resources