I really hope this question doesn't end marked as 'already answered' because I've minutely checked previous questions and my case, unfortunately, doesn't happen to be there.
I've already create a (working) jQuery function to transform a form into a JSON object so I can send it to the server (this is all with an educational purpose by the way, so please save your time before telling me to use a library lol) and even though is working great, I want to save it in a separate file so my main script doesn't look too awful.
(function ($) {
$.fn.formToJSON = function(){
let json = {};
if($(this).is('form')){
$.each($(this).find('input'), function(){
if(this.name && this.value){ //are not empty
if(this.type === 'checkbox' && this.checked){
json[this.name] = (json[this.name] || []).concat(this.value || ''); //check if exists
}else{
json[this.name]= this.value;
}
}
});
}
console.log(JSON.stringify(json));
}
})(jQuery);
What do I need to do to use it by just importing the script in the HTML file?
The followinglike function patters are not what I'm looking for in my script by the way.
(function($) {
$.fn.helloWorld = function() {
return this.each( function() {
$(this).text("Hello, World!");
});
}
}(jQuery));
I'd appreciate if someone could help me achieve the goal which is using my script like this (I can already use like that but it needs the function to be in the main script):
$('form').formToJSON();
Nevermind, I just figured out all I need to do is add several declarations of functions inside my IIF (Immediately Invoked Function) jQuery function.
(function($){
$.fn.logme = function(){
console.log(this);
}
$.fn.logHi = function(){
console.log('hi!');
}
})(jQuery)
Related
I'm implemeting a very simple use case, and yet not only do I not find a solution, but I can't find any article that talks about it, as if I was the only one.
I want my custom Javascript to execute on every page of a given SharePoint site.
Easy, you'll say. Well, no. Far from it, like always with SharePoint.
Steps to reproduce :
Create a out-of-the-box publishing site
Include the custom javascript below using any of the means I describe below
Go to the site, to the home page. It's a publishing site, so by default you should have the left navigation pane with at least "Home" and "Documents" by default.
The first time you load the page, the javascript executes. Now, click on "documents". The page changes but the Javascript is not executed.
That's because SharePoint uses Ajax. Even if the MDS is disabled. It uses Ajax through the hash ( # ) in the URL.
For example, it transforms a very inocuous link like this one :
< a href src="/SitePages/Home.aspx">
into this URL when you click it:
https://your-url/sites/your-site/_layouts/15/start.aspx#/SitePages/Home.aspx
Here is my Javascript :
if (ExecuteOrDelayUntilScriptLoaded && _spBodyOnLoadFunctionNames) {
_spBodyOnLoadFunctionNames.push(ExecuteOrDelayUntilScriptLoaded(
function () {
alert("It's working!");
}, "sp.js"));
}
So, I've tried the following ways of including the Javascript :
Through a User Custom Action. I've used this very handy page to add it, but that's not relevant. The action is added to the site and I can see the JS in the DOM on first load. But then after I click on a link in the page and after SP uses Ajax, it does not execute it again.
By modifying the master page -- namely: seattle.html. at first I included it this way, simply under other native inclusions :
<head runat="server">
...
<!--SPM:<SharePoint:ScriptLink language="javascript" name="suitelinks.js" OnDemand="true" runat="server" Localizable="false"/>-->
<!--SPM:<SharePoint:ScriptLink language="javascript" Name="~sitecollection/SiteAssets/MYJAVASCRIPT.js" runat="server"/>-->
But then I read about AjaxDelta (here : https://msdn.microsoft.com/fr-fr/library/office/dn456543.aspx ) , and I moved my inclusion (still in the header) into < AjaxDelta >, like this :
<head runat="server">
...
<!--SPM:<SharePoint:AjaxDelta id="DeltaPlaceHolderAdditionalPageHead" Container="false" runat="server">-->
<!--SPM:<asp:ContentPlaceHolder id="PlaceHolderAdditionalPageHead" runat="server"/>-->
<!--SPM:<SharePoint:DelegateControl runat="server" ControlId="AdditionalPageHead" AllowMultipleControls="true"/>-->
<!--SPM:<SharePoint:ScriptLink language="javascript" Name="~sitecollection/SiteAssets/MYJAVASCRIPT.js" runat="server"/>-->
<!--SPM:</SharePoint:AjaxDelta>-->
...and yet nothing works. The Javascript is never executed when switching between pages of the same site by clicking on SharePoint's "managed" links.
I'm looking for a solution that handles elegantly SharePoint's Ajax, not something heavy and risky that hijacks every hyperlink on a page. For example I've tried to hook my code onto ajaxNavigate methods (for example : addNavigate) but I'm not sure I understand what's actualy going on there and if it could be of any help to me.
EDIT :
There seems to be a consensus (for example, here at the very bottom) that User Custom Actions get executed no matter what -- because SharePoint allegedly places their ScriptLink into the AjaxDelta for some reason. Well, that's not what I witnessed.
There's another consensus that this issue can be adressed by using "RegisterModuleInit". This doesn't work for me either.
I'm extermely puzzled. I think those two solutions do address navigation issues when the user clicks on a link and then clicks "back". But it does NOT address SharePoint's clever "managed", Ajax-riddled, hyperlinks.
I've finally found a solution that never seems to fail so far. That's a real relief.
Short answer: use asyncDeltaManager.add_endRequest
This MSDN discussion suggests a simple way to implement it:
https://social.msdn.microsoft.com/Forums/office/en-US/1ae292b4-3589-46f6-bedc-7bd9dc741f1b/javascript-code-to-execute-after-all-the-elements-and-css-are-loaded?forum=appsforsharepoint
$(function () {
ExecuteOrDelayUntilScriptLoaded(function () {
if (typeof asyncDeltaManager != "undefined")
asyncDeltaManager.add_endRequest(MYCUSTOMCODE); //execute it after any ajax event
else
MYCUSTOMCODE(); //execute it at first load
}, "start.js");
});
This shows how to include it properly in SharePoint's cycle (with ExecuteOrDelayUntilScriptLoaded )
https://sharepoint.stackexchange.com/questions/171490/javacript-only-executed-on-first-page-load
Full-blown solution (objet "LefeCycleHelper"), by Mx
https://sharepoint.stackexchange.com/questions/192974/where-to-place-a-js-script-with-whom-i-need-to-get-an-div-id/193009#193009
//use an IIFE to create a scope and dont dirty the global scope
(function (_) {
// use strict to ensure we dont code stupid
'use strict';
var initHandlers = [];
var initMDSHandlers = [];
var ensureSharePoint = function (handler) {
var sodLoaded = typeof (_v_dictSod) !== 'undefined' && _v_dictSod['sp.js'] != null && _v_dictSod['sp.js'].state === Sods.loaded;
if (sodLoaded) {
handler();
} else {
SP.SOD.executeFunc('sp.js', 'SP.ClientContext', function () { });
SP.SOD.executeOrDelayUntilScriptLoaded(handler, 'sp.js');
}
};
var initMDS = function () {
for (var i = 0; i < initMDSHandlers.length; i++) {
initMDSHandlers[i]();
}
};
var init = function () {
// Register MDS handler
if ('undefined' != typeof g_MinimalDownload && g_MinimalDownload && (window.location.pathname.toLowerCase()).endsWith('/_layouts/15/start.aspx') && 'undefined' != typeof asyncDeltaManager) {
asyncDeltaManager.add_endRequest(initMDS);
} else {
for (var i = 0; i < initHandlers.length; i++) {
initHandlers[i]();
}
}
};
var registerInit = function (handler) {
initHandlers.push(handler);
};
var registerInitMDS = function (handler) {
initMDSHandlers.push(handler);
};
var domReady = (function (handler) {
var fns = [];
var listener;
var loaded = (document.documentElement.doScroll ? /^loaded|^c/ : /^loaded|^i|^c/).test(document.readyState);
if (!loaded) {
document.addEventListener('DOMContentLoaded', listener = function () {
document.removeEventListener('DOMContentLoaded', listener);
loaded = 1;
while (listener = fns.shift()) listener();
});
}
return function (fn) {
loaded ? setTimeout(fn, 0) : fns.push(fn);
};
})();
var attachToLoad = function (functionToAttach) {
registerInit(functionToAttach);
registerInitMDS(functionToAttach);
domReady(function () {
init();
});
};
_.AttachToLoad = attachToLoad;
// THIS WILL PROTECT YOUR GLOBAL VAR FROM THE GARBAGE COLLECTOR
window.LifeCycleHelper = _;
if (window.Function != 'undefined' && typeof (Function.registerNamespace) == 'function') {
Function.registerNamespace('LifeCycleHelper');
}
})({});
var theCodeYouWantToRun = function () {
alert('theCodeYouWantToRun');
};
window.LifeCycleHelper.AttachToLoad(theCodeYouWantToRun);
I have two similar functions, function done and function delete.
I want to merge them together but I don't know how to merge them properly.
I did it in the back end for php but for javascript somehow I couldn't get it to work.
I have jquery for these two buttons let's say
I started with something like this which works fine and nothing wrong but I guess it'll be good for to merge them together since they are pretty similar.
$('ol#textField').on('click', '.done-btn', doneButton);
$('ol#textField').on('click', '.delete-btn', deleteButton);
my deleteButton and doneButtion functions
function deleteButton(e){
e.preventDefault();
var $clicked = $(this);
var $cLI = $clicked.closest('li');
var todoText = $cLI.clone().children().remove().end().text();
var getID = $cLI.attr('id');
$.ajax({
// codes
}
});
}
function doneButton(e){
e.preventDefault();
var $clicked = $(this);
var $cLI = $clicked.closest('li');
var $cSpan = $clicked.closest('span');
var todoText = $cLI.clone().children().remove().end().text();
var getID = $cLI.attr('id');
$.ajax({
//codes
}
});
}
as seen they are like the same but of course except the ajax part which I didn't add in since it'll be too much codes and I don't think those codes is any concern.
so I tried something like this to combine them but doesn't work.
$('ol#textField').on('click', '.done-btn', doubleD('done'));
$('ol#textField').on('click', '.delete-btn', doubleD('delete'));
I tried to combine the function like this. so if the parameter is done then the done ajax will be called and parameter is delete then delete will be called. I also want to add the .preventDefault() into the function but have no clue how to get them done.
function doubleD(action){
var $clicked = $(this);
var $cLI = $clicked.closest('li');
var todoText = $cLI.clone().children().remove().end().text();
var getID = $cLI.attr('id');
if(action == 'done'){
var $cSpan = $clicked.closest('span');
$.ajax({
// ajax for done
}
});
}
if(action == 'delete'){
$.ajax({
// ajax for delete
}
});
}
}
Can someone please give me a hand?
Thank you for your time and attention.
The problem is that jQuery doesn't pass your argument on to the handler. You have to add that as event data. Reference: http://api.jquery.com/on/
Try this:
$('ol#textField').on('click', '.done-btn', { action: 'done' }, doubleD);
$('ol#textField').on('click', '.delete-btn', { action: 'delete' }, doubleD);
Then you will access like this:
function doubleD(evt){
var action = evt.data.action; // ACCESS THE PARAMETER HERE
var $clicked = $(this);
var $cLI = $clicked.closest('li');
var todoText = $cLI.clone().children().remove().end().text();
var getID = $cLI.attr('id');
if(action == 'done'){
var $cSpan = $clicked.closest('span');
$.ajax({
// ajax for done
}
});
}
if(action == 'delete'){
$.ajax({
// ajax for delete
}
});
}
}
I don't recommend doing it this way, but you can also write it like this:
$('ol#textField').on('click', '.done-btn', function () {
doubleD('done');
});
$('ol#textField').on('click', '.done-btn', function () {
doubleD('delete');
});
Then, your original doubleD function would work.
have you simply tried this
$('ol#textField').on('click', '.done-btn, .delete-btn', function(e){
e.preventDefault();
if( $(this).hasClass('done-btn') ){
}else{
}
});
IF not $(this) you can use $(e.target) i am pretty sure too, checking the class will tell you the button, no?
To separate the call, the main difference is the scope it's being called in. When it's part of the event, then the $(this) has meaning, if it's separate then you lose that scope. To overcome that you can use the event object which contains the target of the event http://www.w3schools.com/jquery/event_target.asp, you have to be mind full of event bubbling though but I think in this case ( using an input ) you would be ok, you could check $(e.target).is('input[type="button"]') if you really wanted to be safe. Anyway:
$('ol#textField').on('click', '.done-btn, .delete-btn', doubleD);
function doubleD(e){
e.preventDefault();
if( $(e.target).hasClass('done-btn') ){
}else{
}
};
However as I said int the comments, separating the logic from the presentation ( using e.data ) has great value. What that means is that you are not relying on the class names for the logic. So if at a latter point you decide to change the class you don't need to update the code, because it's not relying on your presentation ( page layout and styling ).
I actually know about the other answer and planed on adding it as well, but as I don't use it much I had to do a bit of googling to make sure I remembered it correctly. The other poster beat me to it. Which is fine, but I wanted to point out it's actually a better method.
Also you could use a data- http://www.w3schools.com/tags/att_global_data.asp attribute instead of the class, like
<input type="button" class="done-btn" data-mode="done" />
And check it by doing
if( $(this).data('mode') == 'done' ){
...
Some may say that is not the "best practice" way of doing it so really the best way, still is to use the event.data. Because while it be less relent on the presentation, there is still that element of dependency on the DOM element.
Java script in Html is as follows.
$(document).ready(function () {
var found = {{.found}}
window.alert("hiiii");
if (foundRecords==true) {
document.getElementById("abc").style.display = "block";
}
return
});
This should get loaded during the time of html loading. But it's not at all loading. I didn't find anything wrong in this simple peace of code.
if you want to get elements with class found {{.found}}
window.onload = function()
{
var found = document.getElementsByClassName("found");
if (found) {
document.getElementById("abc").style.display = "block";
}
}
If you use jQuery to load that function it will take a slight change:
$(document).ready(function () {
// get the class found and assign it to a variable found
var found = $('.found') // it was {{.found}} producing an error
window.alert("hiiii");
// where does foundRecords come from? it is up to you to clear this
if ( foundRecords == true ) {
document.getElementById("abc").style.display = "block";
}
// what is the return good for?
// it would be better to return true or false
// or leave it away
return;
});
Check the jsFiddle:
http://jsfiddle.net/bx0e18L4/
Now it alerts the message, but still has the problem with the variable foundRecords. Take care for that.
EDIT:
According to your comments above the variable foundRecords should be found, so the critical line should be:
if ( found == true ) { // processing }
Below is my snippet of code, intended to show the comments of a certain thread that's selected.
$('.comments-count').click(function(){
if(!commentsDown){
$(this).parent().parent().siblings('.comments').stop().slideDown();
commentsDown = true;
currentlyDown = $(this).parent().parent().siblings('.comments');
}else{
$(currentlyDown).stop().slideUp();
var newDown = $(this).parent().parent().siblings('.comments');
if(newDown != currentlyDown){
$(this).parent().parent().siblings('.comments').stop().slideDown();
commentsDown = true;
currentlyDown = $(this).parent().parent().siblings('.comments');
}else{
commentsDown = false;
currentlyDown = null;
}
}
})
The line $(currentlyDown).stop().slideUp(); works if you post it into the console, but for some reason it's ignored in this script. I put in console.log() commands and it showed that it definitely should execute it.
commentsDown and currentlyDown are global variables, initially set to false and null respectively.
Here's a JSFiddle. The threads are currently static HTML. As you can see, if you open a thread and then open a different one it works fine, but it doesn't work to close a thread.
You should be able to reduce your whole block of code to:
$(document).ready(function () {
$('.comments-count').click(function () {
$('.comments-count').not($(this)).parent().parent().siblings('.comments').stop().slideUp();
$(this).parent().parent().siblings('.comments').stop().slideToggle();
})
//Log colour pattern
$('div.event-log-entry:even').addClass('evens');
$('div.event-log-entry:even .comments-count').addClass('evens');
})
jsFiddle example
Add your function inside document.ready tags;
$(document).ready(function () {
//insert your code here
});
For more info. go on this site:
http://learn.jquery.com/using-jquery-core/document-ready/
Hope it helps :)
I know this has been asked multiple times, but neither of the answers seem to help me.
I've been almost two days trying to get around this but I haven't been able to figure out what's going on.
I have the following code:
alert('Before document.ready');
$(document).ready(function () {
alert('Actual document.ready');
addNumberValidation($("#quantity"), $("#quantityError"));
addNumberValidation($("#price"), $("#priceError"));
$("#form").submit(function(){
var quantityValid = validar( $("#quantity"), $("#quantityError") );
var priceValid= validar( $("#price"), $("#priceError"));
var formValid = quantityValid && priceValid;
return formValid ;
});
});
function addNumberValidation(mainElement, errorElement) {
mainElement.keyup(function () {
validate($(this), errorElement);
});
}
function validate( mainElement, errorElement) {
var regex = /^[0-9]+$/;
var result = false;
if ( mainElement.val().match(regex)) {
errorElement.text('');
result = true;
} else {
errorElement.text('Must be a number');
result = true;
}
return result;
}
The script is getting loaded correctly because the "Before document.ready" alert is getting called correctly. Also, jQuery is getting loaded as well because other js code is executing properly.
My console shows no error whatsoever and the script under the sources tab in Chrome is complete.
I documented the functions to see if there was something wrong with that and it still didn't work.
Any insights of what could be going on?
Found the issue. Another library was making a conflict that avoided the document.ready to get called