Polymer bounded property not updating (sometimes) - javascript

This problem is with 1.0
I have the following html in a polymer element
<iron-selector attr-for-selected="step-number" selected="{{stepNumber}}">
<section step-number="1">
<page-1 id="page1"></page-1>
</section>
<section step-number="2">
<page-2 id="page2"></page-2>
</section>
<section step-number="3">
<page-3 id="page3"></page-3>
</section>`
page 1 fires an event - event1, page-2 fires event2. Here's my javascript for the polymer element
function () {
Polymer({
is: 'name',
ready: function() {
this.stepNumber = 1;
var firstStep = this.$.page1;
var secondStep = this.$.page2;
var thirdStep = this.$.page3;
var that = this;
firstStep.addEventListener('event1', function(e) {
that.stepNumber = 2; // WORKING
});
secondStep.addEventListener('event2', function(e) {
that.stepNumber = 3; // NOT WORKING
});
}
});
Here's the interesting part:
Both events get fired, the value for stepNumber gets updated inside event1 handler but not inside event2 handler. I also debugged this and inside the event2 handler the value of stepNumber DOES get updated (page-3 element gets the tag "iron-selected") but it doesn't persist. After a bunch of polymer code executes the value is back to 2.
Looks like a bug in polymer?
Some info from the debugger -
polymer code that's executing after the event method is basically the code that executes the event handler, followed by a "gesture recognition" code (which i guess is the on-click), where the tag iron-selector is added to page-2 section
var recognizers = Gestures.recognizers;
for (var i = 0, r; i < recognizers.length; i++)
{
r = recognizers[i];
if (gs[r.name] && !handled[r.name]) {
handled[r.name] = true; // in here for r.name = "tap"
r[type](ev); // type = click. ev = MouseEvent. ev.target = iron-selector
}
}
Digging deeper -
this creates an event handler
_createEventHandler: function(node, eventName, methodName) {
var host = this;
return function(e) {
if (host[methodName]) {
hostmethodName;
} else {
host._warn(host._logf("_createEventHandler", "listener method " + methodName + " not defined"));
}
};
host is iron-selector. methodName is _activateHandler where _itemActivate is called for section of page-2
_activateHandler: function(e) {
// TODO: remove this when https://github.com/Polymer/polymer/issues/1639 is fixed so we
// can just remove the old event listener.
if (e.type !== this.activateEvent) {
return;
}
var t = e.target;
var items = this.items;
while (t && t != this) {
var i = items.indexOf(t);
if (i >= 0) {
var value = this._indexToValue(i);
this._itemActivate(value, t);
return;
}
t = t.parentNode;
}
},
_itemActivate: function(value, item) {
if (!this.fire('iron-activate',
{selected: value, item: item}, {cancelable: true}).defaultPrevented) {
this.select(value);
}
_itemActivate is where the value of stepNumber changes to 2

Clear the activateEvent property (activate-event attribute) of the iron-selector, like this:
<iron-selector attr-for-selected="step-number" selected="{{stepNumber}}" activate-event="">

Try this:
Polymer({
is: 'name',
ready: function() {
this.stepNumber = 1;
var firstStep = this.$.page1;
var secondStep = this.$.page2;
var thirdStep = this.$.page3;
var that = this;
firstStep.addEventListener('event1', function(e) {
that.async(function(){
that.stepNumber = 2;
},0);
});
secondStep.addEventListener('event2', function(e) {
that.async(function(){
that.stepNumber = 3;
},0);
});
}
});

Related

Jquery plugin is not working and causing console errors, how do I fix?

I installed Shuffle.js from a codepen demo with some customizations to style and figure cards. I've added in recommended js code but the shuffle function doesn't seem work.
Getting several errors:
console errors
I've tried updating script based on some past answers to this problem. I've also downloaded and added the actual js file on my site and referenced it in the script. Here's what my script looks like right now:
<script src="/v/vspfiles/assets/js/shuffle.js"></script>
<script>
'use strict';
var Shuffle = window.shuffle;
var Demo = function (element) {
this.element = element;
// Log out events.
this.addShuffleEventListeners();
this.shuffle = new Shuffle(element, {
itemSelector: '.picture-item',
sizer: element.querySelector('.my-sizer-element'),
});
this._activeFilters = [];
this.addFilterButtons();
this.addSorting();
this.addSearchFilter();
this.mode = 'exclusive';
};
Demo.prototype.toArray = function (arrayLike) {
return Array.prototype.slice.call(arrayLike);
};
Demo.prototype.toggleMode = function () {
if (this.mode === 'additive') {
this.mode = 'exclusive';
} else {
this.mode = 'additive';
}
};
/**
* Shuffle uses the CustomEvent constructor to dispatch events. You can listen
* for them like you normally would (with jQuery for example). The extra event
* data is in the `detail` property.
*/
Demo.prototype.addShuffleEventListeners = function () {
var handler = function (event) {
console.log('type: %s', event.type, 'detail:', event.detail);
};
this.element.addEventListener(Shuffle.EventType.LAYOUT, handler, false);
this.element.addEventListener(Shuffle.EventType.REMOVED, handler, false);
};
Demo.prototype.addFilterButtons = function () {
var options = document.querySelector('.filter-options');
if (!options) {
return;
}
var filterButtons = this.toArray(
options.children
);
filterButtons.forEach(function (button) {
button.addEventListener('click', this._handleFilterClick.bind(this), false);
}, this);
};
Demo.prototype._handleFilterClick = function (evt) {
var btn = evt.currentTarget;
var isActive = btn.classList.contains('active');
var btnGroup = btn.getAttribute('data-group');
// You don't need _both_ of these modes. This is only for the demo.
// For this custom 'additive' mode in the demo, clicking on filter buttons
// doesn't remove any other filters.
if (this.mode === 'additive') {
// If this button is already active, remove it from the list of filters.
if (isActive) {
this._activeFilters.splice(this._activeFilters.indexOf(btnGroup));
} else {
this._activeFilters.push(btnGroup);
}
btn.classList.toggle('active');
// Filter elements
this.shuffle.filter(this._activeFilters);
// 'exclusive' mode lets only one filter button be active at a time.
} else {
this._removeActiveClassFromChildren(btn.parentNode);
var filterGroup;
if (isActive) {
btn.classList.remove('active');
filterGroup = Shuffle.ALL_ITEMS;
} else {
btn.classList.add('active');
filterGroup = btnGroup;
}
this.shuffle.filter(filterGroup);
}
};
Demo.prototype._removeActiveClassFromChildren = function (parent) {
var children = parent.children;
for (var i = children.length - 1; i >= 0; i--) {
children[i].classList.remove('active');
}
};
Demo.prototype.addSorting = function () {
var menu = document.querySelector('.sort-options');
if (!menu) {
return;
}
menu.addEventListener('change', this._handleSortChange.bind(this));
};
Demo.prototype._handleSortChange = function (evt) {
var value = evt.target.value;
var options = {};
function sortByDate(element) {
return element.getAttribute('data-created');
}
function sortByTitle(element) {
return element.getAttribute('data-title').toLowerCase();
}
if (value === 'date-created') {
options = {
reverse: true,
by: sortByDate,
};
} else if (value === 'title') {
options = {
by: sortByTitle,
};
}
this.shuffle.sort(options);
};
// Advanced filtering
Demo.prototype.addSearchFilter = function () {
var searchInput = document.querySelector('.js-shuffle-search');
if (!searchInput) {
return;
}
searchInput.addEventListener('keyup', this._handleSearchKeyup.bind(this));
};
/**
* Filter the shuffle instance by items with a title that matches the search input.
* #param {Event} evt Event object.
*/
Demo.prototype._handleSearchKeyup = function (evt) {
var searchText = evt.target.value.toLowerCase();
this.shuffle.filter(function (element, shuffle) {
// If there is a current filter applied, ignore elements that don't match it.
if (shuffle.group !== Shuffle.ALL_ITEMS) {
// Get the item's groups.
var groups = JSON.parse(element.getAttribute('data-groups'));
var isElementInCurrentGroup = groups.indexOf(shuffle.group) !== -1;
// Only search elements in the current group
if (!isElementInCurrentGroup) {
return false;
}
}
var titleElement = element.querySelector('.picture-item__title');
var titleText = titleElement.textContent.toLowerCase().trim();
return titleText.indexOf(searchText) !== -1;
});
};
document.addEventListener('DOMContentLoaded', function () {
window.demo = new Demo(document.getElementById('grid'));
});
</script>
Any insight into what I need to remedy to get this working properly would be great. Thanks!

How to call dragscroll function manually after pageload?

I have below code for dragscroll which runs automatically when page load.
But I want to call this function manually because I'm uppending the html using DOM element.
I have tried with window.factory as well as window.reset but it is not working for me.
My Javascript module file:
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
define(['exports'], factory);
} else if (typeof exports !== 'undefined') {
factory(exports);
} else {
factory((root.dragscroll = {}));
}
}(this, function (exports) {
var _window = window;
var _document = document;
var mousemove = 'mousemove';
var mouseup = 'mouseup';
var mousedown = 'mousedown';
var EventListener = 'EventListener';
var addEventListener = 'add'+EventListener;
var removeEventListener = 'remove'+EventListener;
var newScrollX, newScrollY;
var dragged = [];
var reset = function(i, el) {
for (i = 0; i < dragged.length;)
{
<some code>
}
// cloning into array since HTMLCollection is updated dynamically
dragged = [].slice.call(_document.getElementsByClassName('dragscroll'));
for (i = 0; i < dragged.length;)
{
<some code>
}, 0
);
_window[addEventListener](
mouseup, cont.mu = function() {pushed = 0;}, 0
);
_window[addEventListener](
mousemove,
cont.mm = function(e) {
if (pushed) {
(scroller = el.scroller||el).scrollLeft -=
newScrollX = (- lastClientX + (lastClientX=e.clientX));
scroller.scrollTop -=
newScrollY = (- lastClientY + (lastClientY=e.clientY));
if (el == _document.body) {
(scroller = _document.documentElement).scrollLeft -= newScrollX;
scroller.scrollTop -= newScrollY;
}
}
}, 0
);
})(dragged[i++]);
}
}
if (_document.readyState == 'complete') {
reset();
} else {
_window[addEventListener]('load', reset, 0);
}
exports.reset = reset;
}));
})
I want to call above function manually.
Can you try with
window.dragscroll.reset();
this will call your reset function manually.
Let me know if it helps.
This looks like a standard JavaScript module that exports a reset() method. Depending on your environment you can load this module, e.g with CommonJS it would be
const module = require('name/path/of_your_module_file');
and then to call it when the DOM load is complete with jQuery:
$(() => {
module.reset();
});

JavaScript + jQuery + Angular: Element is not found

I have a JavaScript function defined. Inside this function, I define variables of type jQuery elements. So, the variables refer to divs on the HTML. This function returns an object that has a single function init().
In the $(document).ready function, I call init() function.
The problem is when the script loads, the DOM is not ready and hence the variables referring to jQuery items are being set to undefined.
Later, I call the init() function inside Angular ngOnInit() to make sure things are well initialized, so nothing is happening as the variables above are undefined and they are not being re-calculated again.
Seems, when a function in JavaScript is defined, its body runs, and hence the variables are run and set to undefined as the HTML elements were not in the DOM yet.
How can I re-calculate the variables when init() runs? I cannot get my mind on this thing.
Thanks
var mQuickSidebar = function() {
var topbarAside = $('#m_quick_sidebar');
console.log('Function: ', Date.now());
var topbarAsideTabs = $('#m_quick_sidebar_tabs');
var topbarAsideClose = $('#m_quick_sidebar_close');
var topbarAsideToggle = $('#m_quick_sidebar_toggle');
var topbarAsideContent = topbarAside.find('.m-quick-sidebar__content');
var initMessages = function() {
var messenger = $('#m_quick_sidebar_tabs_messenger');
if (messenger.length === 0) {
return;
}
var messengerMessages = messenger.find('.m-messenger__messages');
var init = function() {
var height = topbarAside.outerHeight(true) -
topbarAsideTabs.outerHeight(true) -
messenger.find('.m-messenger__form').outerHeight(true) - 120;
// init messages scrollable content
messengerMessages.css('height', height);
mApp.initScroller(messengerMessages, {});
}
init();
// reinit on window resize
mUtil.addResizeHandler(init);
}
var initSettings = function() {
var settings = $('#m_quick_sidebar_tabs_settings');
if (settings.length === 0) {
return;
}
// init dropdown tabbable content
var init = function() {
var height = mUtil.getViewPort().height - topbarAsideTabs.outerHeight(true) - 60;
// init settings scrollable content
settings.css('height', height);
mApp.initScroller(settings, {});
}
init();
// reinit on window resize
mUtil.addResizeHandler(init);
}
var initLogs = function() {
// init dropdown tabbable content
var logs = $('#m_quick_sidebar_tabs_logs');
if (logs.length === 0) {
return;
}
var init = function() {
var height = mUtil.getViewPort().height - topbarAsideTabs.outerHeight(true) - 60;
// init settings scrollable content
logs.css('height', height);
mApp.initScroller(logs, {});
}
init();
// reinit on window resize
mUtil.addResizeHandler(init);
}
var initOffcanvasTabs = function() {
initMessages();
initSettings();
initLogs();
}
var initOffcanvas = function() {
topbarAside.mOffcanvas({
class: 'm-quick-sidebar',
overlay: true,
close: topbarAsideClose,
toggle: topbarAsideToggle
});
// run once on first time dropdown shown
topbarAside.mOffcanvas().one('afterShow', function() {
mApp.block(topbarAside);
setTimeout(function() {
mApp.unblock(topbarAside);
topbarAsideContent.removeClass('m--hide');
initOffcanvasTabs();
}, 1000);
});
}
return {
init: function() {
console.log('Inside Init(): ', Date.now());
console.log($('#m_quick_sidebar')); // topbarAside is undefined here!
if (topbarAside.length === 0) {
return;
}
initOffcanvas();
}
}; }();
$(document).ready(function() {
console.log('document.ready: ', Date.now());
mQuickSidebar.init();
});
The problem is that you immediately invoke your function mQuickSidebar not
Seems, when a function in JavaScript is defined, its body runs
var mQuickSidebar = function() {
var topbarAside = $('#m_quick_sidebar');
console.log('Function: ', Date.now());
var topbarAsideTabs = $('#m_quick_sidebar_tabs');
var topbarAsideClose = $('#m_quick_sidebar_close');
var topbarAsideToggle = $('#m_quick_sidebar_toggle');
var topbarAsideContent = topbarAside.find('.m-quick-sidebar__content');
...
}(); // <---- this runs the function immediately
So those var declarations are run and since the DOM is not ready those selectors don't find anything. I'm actually surprised that it does not throw a $ undefined error of some sort
This looks like a broken implementation of The Module Pattern
I re-wrote your code in the Module Pattern. The changes are small. Keep in mind that everything declared inside the IIFE as a var are private and cannot be accessed through mQuickSidebar. Only the init function of mQuickSidebar is public. I did not know what you needed to be public or private. If you need further clarity just ask.
As a Module
var mQuickSidebar = (function(mUtil, mApp, $) {
console.log('Function: ', Date.now());
var topbarAside;
var topbarAsideTabs;
var topbarAsideClose;
var topbarAsideToggle;
var topbarAsideContent;
var initMessages = function() {
var messenger = $('#m_quick_sidebar_tabs_messenger');
if (messenger.length === 0) {
return;
}
var messengerMessages = messenger.find('.m-messenger__messages');
var init = function() {
var height = topbarAside.outerHeight(true) -
topbarAsideTabs.outerHeight(true) -
messenger.find('.m-messenger__form').outerHeight(true) - 120;
// init messages scrollable content
messengerMessages.css('height', height);
mApp.initScroller(messengerMessages, {});
};
init();
// reinit on window resize
mUtil.addResizeHandler(init);
};
var initSettings = function() {
var settings = $('#m_quick_sidebar_tabs_settings');
if (settings.length === 0) {
return;
}
// init dropdown tabbable content
var init = function() {
var height = mUtil.getViewPort().height - topbarAsideTabs.outerHeight(true) - 60;
// init settings scrollable content
settings.css('height', height);
mApp.initScroller(settings, {});
};
init();
// reinit on window resize
mUtil.addResizeHandler(init);
};
var initLogs = function() {
// init dropdown tabbable content
var logs = $('#m_quick_sidebar_tabs_logs');
if (logs.length === 0) {
return;
}
var init = function() {
var height = mUtil.getViewPort().height - topbarAsideTabs.outerHeight(true) - 60;
// init settings scrollable content
logs.css('height', height);
mApp.initScroller(logs, {});
};
init();
// reinit on window resize
mUtil.addResizeHandler(init);
};
var initOffcanvasTabs = function() {
initMessages();
initSettings();
initLogs();
};
var initOffcanvas = function() {
topbarAside.mOffcanvas({
class: 'm-quick-sidebar',
overlay: true,
close: topbarAsideClose,
toggle: topbarAsideToggle
});
// run once on first time dropdown shown
topbarAside.mOffcanvas().one('afterShow', function() {
mApp.block(topbarAside);
setTimeout(function() {
mApp.unblock(topbarAside);
topbarAsideContent.removeClass('m--hide');
initOffcanvasTabs();
}, 1000);
});
};
return {
init: function() {
console.log('Inside Init(): ', Date.now());
console.log($('#m_quick_sidebar')); // topbarAside is undefined here!
topbarAside = $('#m_quick_sidebar');
topbarAsideTabs = $('#m_quick_sidebar_tabs');
topbarAsideClose = $('#m_quick_sidebar_close');
topbarAsideToggle = $('#m_quick_sidebar_toggle');
topbarAsideContent = topbarAside.find('.m-quick-sidebar__content');
if (topbarAside.length === 0) {
return;
}
initOffcanvas();
}
};
})(mUtil, mApp, $);
$(document).ready(function() {
console.log('document.ready: ', Date.now());
mQuickSidebar.init();
});

After first click of radio button can not fire change event

My jquery change event of radio button not working after first click and does not give any error please help me. i stuck in this from last week.and also cant count proper value because of this problem.
$('input[name="emailing"]').change(function()
{
checkValue();
});
function checkValue(evt) {
alert('hi');
var package = $("#package_value").val();
var emailing = $('input[name="emailing"]:checked').val();
$('input[name="emailing"]').on("mousedown", function() {
var select = $('input[name="emailing"]:checked').val();
$("#selected").val(select);
}).on("mouseup", function() {
$('input[name="emailing"]').prop('checked', false);
$(this).prop('checked', true).checkboxradio("refresh");
});
var selected = $("#selected").val();
alert(selected);
var update = $("#update").val();
if(update != '')
{
var hiddenPackage = $("#hidden_pricing").val();
var hiddenRadion = $("#hidden_radio").val();
var totalValue = package - hiddenRadion;
if(emailing == 1)
{
$("#package_value").val('');
var value = Number(totalValue) + 38;
$("#package_value").val(value);
$("#hidden_package").val(value);
}
if(emailing == 2)
{
$("#package_value").val('');
var value = Number(totalValue) + 55;
$("#package_value").val(value);
$("#hidden_package").val(value);
}
if(emailing == 0)
{
$("#package_value").val('');
var value = Number(totalValue) + 0;
$("#package_value").val(value);
$("#hidden_package").val(value);
}
}
}
You should use delegate
$(document).delegate( "input[name='emailing']", "change", function() {
checkValue();
});
Use on() function with any element id instead of (document)
$(document).on('change', 'input[name="emailing"]', function() {
checkValue();
});

Adding click listeners onto code generated in a Javascript object

I am creating a Javascript/jQuery slider class which builds a slideshow on a page when invoked.
Here is my Cycler object code:
//Cycle
var Cycler = function(options) {
this.cycleElement = options.cycleElement;
this.speed = options.speed || 5000;
this.effect = options.effect || "linear";
this.currentItem = null;
this.children = this.addItemsToCycler();
this.buttons = null;
if(options.buttonsEl.length != "") {
this.buttons = this.generateButtons();
}
}
Cycler.prototype.addItemsToCycler = function() {
var children = [];
jQuery(this.cycleElement + " > div.cycle-object").each(function(item, value) {
children.push(value);
});
return children;
}
Cycler.prototype.generateButtons = function() {
var buttons = "<ul>";
var counter = 0;
this.children.forEach(function(item) {
buttons += "<li><a href='#' data-rel='" + counter + "'></a></li>";
counter++;
});
buttons += "</ul>";
jQuery("#cycle-buttons").html(buttons);
}
Cycler.prototype.showItem = function() {
jQuery(this.children).fadeOut("slow");
jQuery(this.children[this.currentItem]).fadeIn("slow");
}
Cycler.prototype.start = function() {
if(this.currentItem == null || this.children.length <= this.currentItem) {
this.currentItem = 0;
}
this.showItem();
var self = this;
interval = window.setInterval(function() {
self.next();
}, this.speed);
}
Cycler.prototype.next = function() {
if(this.currentItem >= (this.children.length - 1)) {
this.currentItem = 0;
} else {
this.currentItem++;
}
this.showItem();
}
Cycler.prototype.prev = function() {
if(this.currentItem <= 0) {
this.currentItem = this.children.length - 1;
} else {
this.currentItem--;
}
this.showItem();
}
Cycler.prototype.pause = function() {
if(this.interval == null) {
console.log("Cycler is already paused");
} else {
window.clearInterval(this.interval);
this.interval = null;
}
}
Cycler.prototype.gotoItem = function(gotoItem) {
this.currentItem = gotoItem;
this.showItem();
}
The markup:
<div id="cycle-wrap">
<div id="background-cycle">
<div data-rel="0" class="cycle-object"></div>
<div data-rel="1" class="cycle-object"></div>
<div data-rel="2" class="cycle-object"></div>
</div>
<div class="inner">
<div id="cycle-buttons">
</div>
</div>
</div>
And my client code which creates an instance of the class:
//Cycler
var cycler = new Cycler({
cycleElement : "#background-cycle",
speed : 5000,
effect : "linear",
buttonsEl : "#cycle-buttons"
});
cycler.start();
Now, if a user supplies an element in the 'buttonsEl' variable which is passed to the class, I want the class to generate a list of li elements which when clicked will take the user to that slide. I have done this via the 'generateButtons' function I have already created the prototype function for this called 'goToItem' as you can see above. I am linking the li elements and the corresponding html using a 'data'rel' attribute.
What I would like to do is add click listeners onto each list element (that is generated in the 'generateButtons' function) which will pass the data-rel attribute through to my 'goToItem' function. Does anyone know of the best way this would be incorporated into my code, keeping it nice and OOP.
Thanks
In your Cycler constructor we could pass the buttonsEl to the generateButtons. Then this method will be able to insert the buttons in the desired element.
// Not sure if you want this comparison. If works by coercion but it's tricky. And it
// will fail if your client doesn't pass a buttonsEl option.
// I would prefer something like this:
// if(options.buttonsEl && options.buttonsEl.length > 0) {
if(options.buttonsEl.length != "") {
this.buttons = this.generateButtons(options.buttonsEl);
}
The generateButtons could be something like this:
Cycler.prototype.generateButtons = function(buttonsEl) {
var $buttons = jQuery("<ul></ul>");
this.children.forEach(function(item, counter) {
$buttons.append("<li><a href='#' data-rel='" + counter + "'></a></li>");
});
// Capture cycler this to be able to invoke the gotoItem method
// within the event handler
var that = this;
$buttons.on('click', 'li', function () {
that.gotoItem($(this).find('a').data('rel'));
});
jQuery(buttonsEl).html($buttons);
return $buttons;
}

Categories

Resources