As of jQuery v3.0 the recommended way to bind DOM-ready-methods is $( myReadyFunction ) without any selector or delegation. Especially $(document).on('ready', myReadyFunction ) is removed in jQuery v3.
I now need to modify/override a specific, pre-existing ready-method before it is executed. I know that I can delay the execution of the ready-event by calling $.holdReady( true ) ... but how can I remove/override my old myReadyFunction ?
Existing code that should not be changed:
myReadyFunction = function() { console.info('old'); }
$( myReadyFunction );
I can add code before or after this part, here are some things I tried and did not work:
adding after
$.off( 'myReadyFunction' );
=> $.off is not a function
adding after
$(document).off( 'myReadyFunction' );
$(document).off( 'ready', 'myReadyFunction' );
=> no error messsage, but both variants don't work
myReadyFunction = function() { console.info('new'); }
$( myReadyFunction );
=> adds the new, overridden function, but does not remove the old one
Related
I have checked all stackoverflow about javascript version version of jquery's on(). But none of them have the ability to catch parameters like real jquery's on().
In official jquery docs about .on() they support .on( events [, selector ] [, data ], handler )
But all javascript equivalent version i found only can do like this .on( events [, selector ], handler ). negleting the data attribute.
I have also checked all stackoverflow about javascript version version of jquery's trigger(). But none of them have the ability to pass parameters like real jquery's trigger().
In official jquery docs about .trigger() they support .trigger( event [, extraParameters ] )
But all javascript equivalent version i found only can do like this .trigger( event ). negleting the extraParameters attribute.
Please do not referencing the current js equivalent of .on & .trigger that you an find online. I have searched it for 3days. None of them can pass/receive parameters like jquery do.
So, what is vanilla javascript version jquery's trigger() & on() but with ability to pass and receive parameters. for example like this code:
$( "#foo" ).on( "click", function( event, parameters ) {
alert( parameters.param1 );
});
$( "#foo").trigger( "click", [ { 'param1': true } ] );
You can trigger a custom event with data using the following function:
// See: http://youmightnotneedjquery.com
const triggerEvent = (el, eventName, data) => {
let event;
if (window.CustomEvent && typeof window.CustomEvent === 'function') {
event = new CustomEvent(eventName, { detail: data });
} else {
event = document.createEvent('CustomEvent');
event.initCustomEvent(eventName, true, true, data);
}
el.dispatchEvent(event);
};
const
fooEl = document.querySelector('#foo'),
onClick = e => console.log(e.target, e.detail.param1);
fooEl.addEventListener('click', onClick);
triggerEvent(fooEl, 'click', { param1: true });
<button id="foo">Foo</button>
<!--
$("#foo").on("click", function(event, parameters) {
alert(parameters.param1);
});
$("#foo").trigger("click", [{
'param1': true
}]);
-->
Pass a function into addEventListener that takes the event parameter and passes on that parameter and whatever else you want to pass:
const doSomething = (event, otherArg) => {
console.log('Target:', event.target);
console.log('otherArg:', otherArg);
};
document.querySelector('button').addEventListener('click', e => doSomething(e, 'foobar'));
<button>click</button>
WooCommerce-tables comes with classes like these, out of the box: shop_table shop_table_responsive cart woocommerce-cart-form__contents. So no table-class, which means no nifty Bootstrap-tables.
Huh!
And since overriding the WooCommerce-templates should only be done when absolutely necessary, then let's solve it with JavaScript!
My entire site it encapsulated by a Vue-div, like so:
<div id="app">
...
<table class="shop_table shop_table_responsive cart woocommerce-cart-form__contents">
...
...
</table>
...
</div>
So initially I wrote this code, to add the table-class to all tables:
let tableSelectors = [
'.some-class table',
'.woocommerce-product-attributes',
'.woocommerce-cart-form > table'
];
for( let t = 0; t < tableSelectors.length; t++ ){
let tables = document.querySelectorAll( tableSelectors[t] );
if( tables ){
for( let i = 0; i < tables.length; i++ ){
tables[i].classList.add( 'table' );
}
}
}
... Putting that in the mounted(){ ... }-section.
That worked! So far so good.
But WooCommerce is using jQuery quite a lot. And on the cart page, if I change the quantity (and press 'Update'), then the table-contents are updated using AJAX. If you're curious how it works, then you can check it out here.
And when that runs, I assume that WooCommerce grabs the initial cart-template and reloads that whole table; without the newly added table-class. Bah humbug!
So how can I solve this?
I can override the WooCommerce ./cart/cart.php-template and add the
class to the template. Seems like quite the overkill for adding a class.
I can scan the DOM for tables every second (or so) and apply the table class, if it's not there. Not cool... Regardless if it's done using jQuery or Vue.
Since the whole table is being replaced in the DOM, then it doesn't work to monitor the current table (using watch(){...} in Vue) and apply the class if it changes, - since it never changes (it's replaced).
I'm unable to find a Hook that I can use.
I also tried using ajaxComplete, but I can see in the network-tab that the XHR-request is firing, but this code here is never doing anything (in the console):
jQuery( document ).ajaxComplete(function( event, xhr, settings ) {
console.log( 'Test' );
});
Any other suggestions?
You could use the Mutation Observer API to listen for changes to a wrapper element's contents and re-apply the table classes.
This example is lifted nearly verbatim from the sample code on MDN. Clicking the button replaces the contents of the div, which you can see from the console output fires the observer callback.
// Select the node that will be observed for mutations
const targetNode = document.getElementById('some-id');
// Options for the observer (which mutations to observe)
const config = {
childList: true,
subtree: true
};
// Callback function to execute when mutations are observed
const callback = function(mutationsList, observer) {
for (let mutation of mutationsList) {
if (mutation.type === 'childList') {
console.log('A child node has been added or removed.');
}
}
};
// Create an observer instance linked to the callback function
const observer = new MutationObserver(callback);
// Start observing the target node for configured mutations
observer.observe(targetNode, config);
function doUpdate() {
targetNode.innerText = Math.random();
}
document.querySelector('button').addEventListener('click', doUpdate);
<div id="some-id">(container)</div>
<button>change</button>
The ajaxComplete() function of jQuery can do what you expected,
I don't know why it's not working for you.
I just open the link of cart page you gave above and paste add the below code in the developer console
and it works as expected after each update of the cart, the "table" class successfully appended to the table as per given selectors.
jQuery( document ).ajaxComplete(function( event, xhr, settings ) {
jQuery('.some-class table, .woocommerce-product-attributes, .woocommerce-cart-form > table').addClass("table");
});
It looks like you have not added the code in the proper place. Since ajaxComplete() function is dependent on jQuery you need to execute the above code after jQuery has been loaded successfully. To do that you can use wp_add_inline_script() function with wp_script_is()
Add the following code in your function.php file, It will add the below script to page after jQuery finish loading.
function my_custom_script() {
if ( ! wp_script_is( 'jquery', 'done' ) ) {
wp_enqueue_script( 'jquery' );
}
wp_add_inline_script( 'jquery-migrate', 'jQuery( document ).ajaxComplete(function( event, xhr, settings ) {
jQuery(".some-class table, .woocommerce-product-attributes, .woocommerce-cart-form > table").addClass("table");
});' );
}
add_action( 'wp_enqueue_scripts', 'my_custom_script');
You can try to change the jquery ajax function
(function ($) {
var _oldAjax = $.ajax;
$.ajax = function (options) {
return _oldAjax(options).done(function (data) {
$('.some-class table, .woocommerce-product-attributes, .woocommerce-cart-form > table').addClass("table");
if (typeof (options.done) === "function")
options.done();
});
};
})(jQuery);
above code must be added before any ajax that is called
i've tested it on your site using the javascript console and actually
jQuery( document ).ajaxComplete(function( event, xhr, settings ) {
console.log( 'Test' );
});
fire pretty well.
maybe you are adding that code on wrong place.. or maybe you have "Group Similar" flagged on javascript console settings and you didn't notice the log
so you could just put your code together like this:
jQuery( document ).ajaxComplete(function( event, xhr, settings ) {
try{
let tableSelectors = [
'.some-class table',
'.woocommerce-product-attributes',
'.woocommerce-cart-form > table'
];
for( let t = 0; t < tableSelectors.length; t++ ){
let tables = document.querySelectorAll( tableSelectors[t] );
if( tables ){
for( let i = 0; i < tables.length; i++ ){
tables[i].classList.add( 'table' );
}
}
}
console.dir("ok");
}catch(ex){console.dir(ex);}
});
or use a jquery like solution like this:
jQuery( document ).ajaxComplete(function( event, xhr, settings ) {
jQuery('.some-class table, .woocommerce-product-attributes, .woocommerce-cart-form > table').addClass("table");
});
i've tested both of them directly on your site and both solutions works pretty well
and of course if you want make it works even at start the same script must be applied to jQuery( document ).ready() too:
jQuery( document ).ready(function() {
jQuery('.some-class table, .woocommerce-product-attributes, .woocommerce-cart-form > table').addClass("table");
});
what i'm trying to do is to show when the function its done/ready the process ... i try this code but didn't work ... i try with ".done" but still didn't work ... how i can see when the function its ready ?
this is the code what i try ...
HTML:
<div class="Load_Div"></div>
<br /><br />
<div class="Result_Data"></div>
Jquery:
function Load_HTML() {
$('.Load_Div').load('file.php', function() {
});
}
Load_HTML().ready( function() {
$( ".Result_Data" ).text( "Function Ready" );
} );
This is the code:
http://jsfiddle.net/w9Xf5/
Only the document has a ready() method, no other elements, functions or whatever has a ready method.
As for your function, that is always ready, but as you're doing ajax, what you're really trying to do is check if the ajax call is finished, and jQuery has built in callbacks and deferreds (which load() does not seem to expose, so done() won't work) for this :
function Load_HTML(callback) {
$('.Load_Div').load('file.php', callback);
}
Load_HTML(function () {
$(".Result_Data").text("Function Ready");
});
You could create your own callback
function Load_HTML( callback ) {
$('.Load_Div').load('file.php', callback );
}
And then
Load_HTML( function() {
$( ".Result_Data" ).text( "Function Ready" );
});
Get rid of the Load_HTML().ready() and insert $( ".Result_Data" ).text( "Function Ready" ); into the callback function from your .load() method.
You could use a Deferred-object and give this a lot of functions like additional loading of php-script to process. After each call of the array of functions the process can be evaluated. Please take a look an this JQuery-API for further informations.
I want to add an event handle to an element that will be created later in DOM.
Basically, what I am trying to do is that, when I click p#one, new element p#two will be created, then I click p#two, tell me "p#two" clicked. However, it doesn't work, I didn't get the console.log result of 'p#two clicked' after I click p#two.
I use on() to add click event to p#two. What do I do wrong?
Thanks.
Below is my example code:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>on() test</title>
<link type="text/css" href="http://localhost/jquery-ui-1.8.20.custom/css/smoothness/jquery-ui-1.8.20.custom.css" rel="stylesheet" />
<script type="text/javascript" src="http://localhost/jquery-ui-1.8.20.custom/js/jquery-1.7.2.min.js"></script>
<script type="text/javascript" src="http://localhost/jquery-ui-1.8.20.custom/js/jquery-ui-1.8.20.custom.min.js"></script>
<script type="text/javascript">
$(document).ready(function() {
$('p#two').on('click', function() {
console.log('p#two clicked');
});
$('p#one').click(function() {
console.log('p#one clicked');
$('<p id="two">two</p>').insertAfter('p#one');
});
}); // end doc ready
</script>
</head>
<body>
<p id="one">one</p>
</body>
</html>
$('body').on('click','p#two', function() {
console.log('p#two clicked');
});
you can also use
$(document).on('click', 'p#two', function() {
});
Read more about .on()
you can also use .delegate()
$('body').delegate('#two', 'click', function() {
});
You can bind the $.on to a parent element that will always exist in dom like this.
$(document).on('click','p#two', function() {
console.log('p#two clicked');
});
Note that: you can replace document with any parent of the element that will always exist in dom, and the closer the parent the better.
Check doc of $.on
Live is depreciated. use $.on instead. Equivalent syntax of $.on for $.live and $.delegate
$(selector).live(events, data, handler); // jQuery 1.3+
$(document).delegate(selector, events, data, handler); // jQuery 1.4.3+
$(document).on(events, selector, data, handler); // jQuery 1.7+
I would suggest you to use $.on for all event handling purposes as all other methods routes through $.on method under the hood.
Check the definition of these functions from jQuery source v.1.7.2
bind: function( types, data, fn ) {
return this.on( types, null, data, fn );
},
unbind: function( types, fn ) {
return this.off( types, null, fn );
},
live: function( types, data, fn ) {
jQuery( this.context ).on( types, this.selector, data, fn );
return this;
},
die: function( types, fn ) {
jQuery( this.context ).off( types, this.selector || "**", fn );
return this;
},
delegate: function( selector, types, data, fn ) {
return this.on( types, selector, data, fn );
},
undelegate: function( selector, types, fn ) {
// ( namespace ) or ( selector, types [, fn] )
return arguments.length == 1? this.off( selector, "**" ) : this.off( types, selector, fn );
}
You can see all methods are using $.on and $.off themselves. So using $.on you can at least save a function call though which isn't that significant most of the cases.
You want to use Jquery.on
$('body').on('click','p#two', function() {
console.log('p#two clicked');
});
I have a javascript function that someone made for me. I am kind of a noob at raw javascript. I use jquery quite often so I have been spoiled. Right now the function runs when someone clicks anywhere on the page because the document selector is used to trigger the function. I want the function to run when a specific id is clicked. I do have jquery installed as well. Anyone have any suggestions?
addEvent('#id', 'click', function(){ does not work
addEvent(document.getElementById("id"), 'click', function(){ does not work
function addEvent(obj, type, fn) {
if ( obj.attachEvent ) {
obj['e'+type+fn] = fn;
obj[type+fn] = function(){obj['e'+type+fn]( window.event );}
obj.attachEvent( 'on'+type, obj[type+fn] );
} else
obj.addEventListener( type, fn, false );
}
addEvent(document, 'click', function(){
seems to work:
http://fiddle.jshell.net/MT3ye/
The jquery way.
$("#objId").click(function(){ doSomething();});
Use document.getElementById('id') instead of document.
You should have a look at this:
https://developer.mozilla.org/en/The_DOM_and_JavaScript
how about using the
addEvent($('#id'), 'click', function(){
Would this work for you ...
$(document).ready(function ()
{
$("#element_id").click ( function(){ ... } );
...
}