Twitter Bootstrap Transition Conflict prototypejs - javascript

I created a fiddle for Collapse feature of Twitter Bootstrap using the markup from the demos on your page that works: http://jsfiddle.net/Rymd7/1/
Then once, I add in a reference to prototypejs collapse functionality stops working on each accordian group after clicking through it a couple of times. http://jsfiddle.net/p5SAy/1/ I'm not sure what the issue is or how to correct it.
Is this a bootstrap issue or is there a way to get around this and have these two js libraries exist on the same page?
I have tried jQuery noConflict with no success, but any help is appreciated.
If you can send me back a working fiddle that would be great...or any insight.
Thanks. -John

You are using jQuery and Prototype simultaneously without jQuery.noConflict(). After
<script type='text/javascript' src='http://code.jquery.com/jquery-1.7.js'></script>
<script type='text/javascript' src="http://ajax.googleapis.com/ajax/libs/prototype/1.7.0/prototype.js"></script>
this line was causing javascript error:
$(window).load(function(){
Revised fiddle: http://fiddle.jshell.net/ymbsr/5/ - open http://fiddle.jshell.net/ymbsr/5/show/ in different browsers.
P.S.
After removing jQuery/Prototype conflict I can see that ih Chrome 21 and Opera 12.02 accordion transition never ends (in bootstrap-collapse.js Collapse.transition initiates transition but complete() is never called). Further invocations of Collapse.show()/hide() on the same element are exiting after if (this.transitioning) return.
In Firefox 15.0.1 accordion works fine.
P.P.S.
This strange behavior is a consequence of two features:
this.$element.trigger('hide') (in Collapse#transition()) tries to invoke $element.hide() if method hide() is present in element - that's by design:
Note: For both plain objects and DOM objects, if a triggered event
name matches the name of a property on the object, jQuery will attempt
to invoke the property as a method if no event handler calls
event.preventDefault(). If this behavior is not desired, use
.triggerHandler() instead.
With Prototype in each browser supporting HTML element prototype extensions $element will definitely have hide() method, which actually hides element via setting element.style.display = 'none'.
In current versions of Chrome and Opera transition end event (one of webkitTransitionEnd, oTransitionEnd, otransitionend) will not fire if element is hidden (has display: none style). Firefox ends its transition more successfully, but also may not fire event under some circumstances:
Note: The "transitionend" event doesn't fire if the transition is aborted because the animating property's value is changed before the transition is completed.
How to fix it:
File a bug for bootstrap-collapse.js - it shouldn't rely only on transition end event
File a bug for bootstrap-collapse.js - its hide event intersects with other frameworks (at least with Prototype, but any other framework extending element prototypes may be affected).
Temporarily patch Collapse#transition() from bootstrap-collapse.js as in http://fiddle.jshell.net/ymbsr/7/ - either disable event triggering or change event names.
jQuery.fn.collapse.Constructor.prototype.transition = function (method, startEvent, completeEvent) {
var that = this
, complete = function () {
if (startEvent.type == 'show') that.reset();
that.transitioning = 0;
that.$element.trigger(completeEvent);
}
//this.$element.trigger(startEvent);
//if (startEvent.isDefaultPrevented()) return;
this.transitioning = 1;
this.$element[method]('in');
(jQuery.support.transition && this.$element.hasClass('collapse')) ?
this.$element.one(jQuery.support.transition.end, complete) :
complete();
};

I am using Magento and have had bootstrap/ prototype problems for years. I tried all of the above without luck. Ha you will not believe what worked!
I moved prototype as one of the first scripts to be loaded and left all of the others to be loaded after. That worked for me. I dont know why I didnt try the earlier.

Related

Polymer 1.5/iOS: How to stop event propagation over iron-pages

We have a one page app which uses iron pages and express-router to navigate. In the browser and on android it works perfectly, on iOS however, we have a bug. The problem occurs if we switch pages by a button press. If the button is exactly over an input text field element (read: in the same position, but on the next iron-page) the input element gains focus directly after the page switch.
We used to have this problem as well with two buttons in the same position but this was solved by changing all on-clicks to on-taps.
Things we have tried so far:
Adding event.stopPropagation to the on-tap event
Including fastclick.js to prevent click delays (this worked partially when on-clicks were still in place but was made obsolete with on-tap)
Note that we have experienced this problem since Polymer 1.0 all through 1.5.
I reproduced your symptoms on an iPad Air 2, and trying e.stopPropagation(), e.preventDefault(), and returning false all had no effect. I'm not sure whether this is actually a Polymer problem.
I have a couple [hacky] workarounds to employ during the page-switch:
Option 1: Delay the page-change by 400ms. If your button has a ripple effect, the delay is masked by the animation.
codepen
Option 2: Disable the input and re-enable it after a 400ms delay. This prevents the input from picking up the tap event, but has the disadvantage that the disabled state could be noticeable (perhaps a lesser evil than your current problem).
codepen
Thanks #tony19, for the input.
We wanted to avoid delays, so I researched a bit more and ultimately fixed the problem. To answer my own question: the ultimate solution did lie in the FastClick library.
Basically what happens is that the tap event is fired immediately, but it doesn't replace the click event. Rather, the click event still fires, but with the original 300ms delay. This delayed click event thus fires on the newly displayed 'page' and triggers the input-field (or button had it been there) at the same x-y coordinates.
Adding the FastClick library once again solves this thanks to some updates in the library. However, it breaks some items that need the original click, such as Google Autocomplete. A basic solution to exclude FastClick is to instead apply it as:
FastClick.attach(document.body, {
excludeNode: 'something', });
This, however, only works for that node and not possible children. As such, to fix everything for input fields with Google's Autocomplete as well is done using:
// Disable FastClick for the children of a google auto-
// complete component.
var needsClick = FastClick.prototype.needsClick;
FastClick.prototype.needsClick = function(target) {
if ( (target.className || '').indexOf('pac-item') > -1 ) {
return true;
} else if ( (target.parentNode.className || '').indexOf('pac-item') > -1) {
return true;
} else {
return needsClick.apply(this, arguments);
}
};
// Remove click delay on iOS. As such, removing differences
// in timing between click and tap, thereby avoiding the
// fall-through problem.
FastClick.attach(document.body);
I will now close this thread, but I thought it'd be nice to leave this as reference for anyone else experiencing the problem.
Understand that it affected Polymer 1.0 to 1.5. Just to confirm that we experienced the same behaviour in Polymer 1.6 and the following fixes it.
_onTap: function(event) {
event.preventDefault();
event.stopPropagation();
}

'transitionend' event does not fire consistently

The following code works inconsistently with Chrome but also Firefox (with 'transitionend'). The function slogan_fade_next is just console.log('end');. I always get the classname applied to the first span element but anything after that is hit-and-miss when I click the refresh button, reload, or anything else.
The class of slogan-fadein applied to slogan[] changes the opacity of the element from zero to one but the callback function fade_setup isn't called consistently.
function fade_setup(){
var el = document.getElementsByClassName('slogan')[0];
el = el.getElementsByTagName('span');
for(var i=0;el[i];i++){
el[i].addEventListener('webkitTransitionEnd',slogan_fade_next,false);
}
el[0].className='slogan-fadein';
}
document.addEventListener('DOMContentLoaded', fade_setup);
instead of
document.addEventListener('DOMContentLoaded', fade_setup);
can you use
document.addEventListener('load', fade_setup)
With your current implementation, the JavaScript may be running before the browser has finished applying styles and, therefore, before any transitions are defined.
The problem is caused by a timing issue with applying the styles and anything else as mentioned by Stephen. The problem is, things aren't settled by the time I try to fire the first fade in so I triggered that with window.onload=slogan_fade_next;. Everything has settled in by the time my first element has done its thing.
I've given absolutely no more thought to this other than "it works" and I'm sure I'll come up with a better way to do this.

Wicket - IE8 - Javascript event listeners arent executed, when tags are dynamically added over ajax

I have spent several hours, maybe days stucked on a very weird problem :(
I am creating an application that is based on the Wicket solution. It works perfectly in IE9,IE10, Chrome and FF. Strange is, that i have tested it in IE8 too and it works in 99% of cases (IE instances on different computers + totally identical version of IE8) too. But now the PROBLEM.
PROBLEM:
I am creating dynamic content over AjaxLink button. After clicking the button the WebMarkupContainer model is changed and WebMarkupContainer is refreshed (based on Ajax, so the page isnt reloaded complete, but only the container is).
Every item in the container has added AjaxFormComponentUpdatingBehavior. In onComponentTag method, i add tag.put("onchange","some jsFunctionCalling....");. The problem is, that after clicking on the item, no event is invoked. I have tried add the onchange listener over .add(new AttributeModifier.....), but the result is still same. As i have said, i tried the same code in the same version of IE on another PC and it works perfectly. Interesting is, that after refreh of the page everything work perfect, until new item to WebMarkupContainer is added. After that no item listeners work until the page is refreshed again.
One of the latest idea, that i got is, that problem isn't in the code, but in the settings of IE (maybe security). Have anybody any idea? What setting could be set different and cause these problems? Is there any settings on Wicket site, that can solved this? Is there some setting that can blocked registration of these listeners to DOM, if they are added dynamically over ajax?
I didn't tried it but IMHO there are three options you can try:
Instead of adding "onchange" by yourself, add OnChangeAjaxBehavior and make all work in wicket. Downside is server roundtrip on every event.
Add data-attributes (AttributeModifier.append("data-param1", "foobar")) to push your parameters into html and call ajaxRequestTarget.appendJavaScript("attachOnChangeHandler()"); after the click event on the AjaxLink. attachOnChangeHandler() should be your js function to add onchange handler to every item which needs it. And over data-attributes you can access your parameters.
Since Wicket 6: To avoid mixing to much js with Wicket, you could subscribe to one of the global AJAX events.
The solution in your case would be almost the same as in 2. Just add a listener in js for "/ajax/call/success"
(see if the call relates to your component by checking the id) and add the onchange handler there.
This is IMHO the best solution without mixing custom js with Wicket.
The solution provided by #peterchon (attaching event handlers higher in the DOM than the elements which are going to be replaced by wicket) would work in every other case, but you have "onchange" which applies only to input, textarea and select elements.
BTW the page is "working" after refresh, since the whole page is rendered and browser can properly attach the handlers.
You can try this method:
/* this will catpure the target that triggered the event */
function getEventTarget( e ) {
e = e || window.event;
return e.target || e.srcElement;
}
function doSomething( e ) {
var that = getEventTarget( e );
if( that.tagName.toLowerCase() === 'a' ) { // specify the target, in this cas <a>
// Do something
}
}
parentElement.onclick = doSomething;
This script basically will capture any event, then will pass the variable of target to the function that will perform something.
Hopefully this will work for you.
You try to achieve something using a non-wicket JavaScript/Ajax way. This is fine, but also makes it very messy.
Please check this fine article about passing parameters from JavaScript to wicket and vice versa. I think it will suit your needs.
http://wickedsource.org/2013/01/07/rolling-your-own-ajax-behavior-with-wicket/

Debug which Javascript runs with an element is clicked on

Consider an element <img id='hitMe' /> which is referenced in a jQuery function with either $('#hitme').click() or possibly $(document).on('click', '#hitme'). How can I find these functions in the Javascript source code, other than grepping for the string hitme? Consider that grepping is not feasible as the string may have an arbitrarily large amount of references, and there may be a very large number of imported Javascript files.
Perhaps there might be a way to have Firebug step into Javascript functions that are run, after page load, on a line-by-line basis? Consider that I don't know where to set a breakpoint because I don't know which function is run.
Here's a solution that catches the moment in which the event is trigger (dispatched) rather than the moment in which the listener is being added (on/click).
It can be done by overriding jQuery.event.dispatch.
for example:
(function($){
var old = $.event.dispatch;
$.event.dispatch = function(event){
if(event.type=='click'){
var handlers = ( $._data( this, "events" ) || {} )[ event.type ] || [];
console.log('handlers', handlers[0]);
}
old.apply(this, arguments);
};
})(jQuery);
In this example I chose to print the content of the function to the console, but it could also be useful to debug it like Omri Sivan suggested, with debugger;. With Chrome you should be able to right click on the function to "Show function definition".
Chrome or Chromium can help you there. Make a right click on the element you want to explore, chose "Inspect element", have a look into the right panel which starts with "Computed Style", "Style"... and at the bottom of it you will find the section "Event Listeners", which gives you information about what events are listened and where in the code it was appended.
I recommend Visual Event bookmarklet. What follows, is a shameless copy-paste from the site:
Visual Event is an open source Javascript bookmarklet which provides debugging information about events that have been attached to DOM elements.
Visual Event shows:
Which elements have events attached to them
The type of events attached to an element
The code that will be run with the event is triggered
The source file and line number for where the attached function was defined (Webkit browsers and Opera only)
http://www.sprymedia.co.uk/article/Visual+Event+2
Try the following (I'm using Chrome/Chrome Dev Tools):
Override jQuery's on/click function with something like:
var old = $.fn.on;
$.fn.on = function() { console.log('called on'); debugger; old.apply(this, arguments);}
Every call to .on() should now pass through you (synchronously) and pause at the 'debugger;' statement.
Once paused, look under 'Call Stack' and locate the function you are looking for (e.g. the function that invoked .on()). You can also add some condition to the overriding function above to make sure not every call to .on() pauses.

jquery event added multiple times

I have a fairly large javascript class that generates an complete ajax-generated application. In one version of the ajax page there are a number of dropdown menus. These menus can get created and destroyed at various points during the life cycle of the application.
This is the behaviour I see:
User opens page version 1: no dropdowns
User goes to page version 2: dropdowns added with jQuery onchange event. Work as intended.
User returns to version 1 of page, dropdowns removed.
User returns to version 2 of page, dropdowns added again (using same element IDs)
dropdowns will now have 'double' event handling, triggering the event for each onchange.
The behaviour I'm struggling with is as follows.
On the initial page load, I add an onchange event:
function myClass(){
//Initiate once for current and future elements.
jQuery(document).on('change',".mydropdowns",
function(e){
self.submitDescriptionChange(this);
}
);
}
myClass.prototype.submitDescriptionChange = function (el){
doSomeAjaxStuff();
}
This works fine, except that each time the user goes to pages version 1 and returns to page version 2, the event gets multiplied. Very quickly you can end up with the event firing 20 times per change event, which in this case creates 20 ajax calls.
Logically, by using jQuery.off() I should be able to avoid this. But what happens instead is that the event is removed from both past and future elements, which means that when I recreate page version 2, the dropdowns won't work.
Every way I have tried this (and I've tried LOADS), I either end up with no event firing, or multiple events firing. I cannot seem to find a way to add/replace the elements whereby the event is only ever fired once.
Any ideas how I can solve this?
UPDATED
Yeah, so it turns out I misdiagnosed the problem. It actually came from repeatedly rebinding a 'hashchange' event, rather than rebinding the onchange event. Apologies for misdirection. Moving to bind() function to somewhere where it only executed once fixed the issue.
Since you do not want .off() to remove your events from other pages, I would suggest using namespaces for your events. For example, something like this:
function myClass(pageno) {
var pref_ev = 'mypage' + pageno + '.' + 'change';
$(document).off(pref_ev).on(pref_ev, ".mydropdowns", function(e) {
self.submitDescriptionChange(this);
});
}
This way, each page will have its own "change" event such as "mypage1.change". The event is still registered normally as a change event; the prefix namespace "mypage1" is used to only perform the .off() call on the right events.
I am not sure what plugin you are using for your dropdown menus but there should be a "destroy" method on that plugin. If you call that when removing the dropdowns that should work. Also, if you are only hiding the second page and not actually removing it from the DOM you dont have to re-invoke the plugin as the plugin will still be saved on the element.

Categories