I am using tag-it so users can create tags for their posts.
It currently lets them type anything but I have a list of banned words in the form of JSON.
How could i implement this into the tagit plugin so when a users typed word matched one of the banned words it throws up an error.
How can i check to see if ui.tag exists in swearWords.json?
Here is my current code:
$('#methodTags_text').tagit({
availableTags: sampleTags,
tagLimit:3,
maximumInputLength: 10,
fieldName: "item[tags][]",
beforeTagAdded: function(event, ui) {
// if ui.tag exists in swearWords.json
// Then output error
$('#banned_error').html('You can use this word.')
}
});
swearWords.json
["word1","word2","word3","word4","word5"]
using François Wahl answer I managed to get a working solution...
$('#methodTags_text').tagit({
availableTags: sampleTags,
tagLimit:3,
maximumInputLength: 10,
removeConfirmation: true,
fieldName: "item[tags][]",
beforeTagAdded: function(event, ui) {
if (!ui.duringInitialization) {
var word = eventTags_text.tagit('tagLabel', ui.tag);
if($.inArray(word , words) > -1){
$('#banned_error').html('You cant use this word.');
$("#methodTags_url").tagit("removeTagByLabel", word);
} else {$('#banned_error').html('');}
}
}
});
ALSO to get external json in var
var words = (function () {
var words = null;
$.ajax({
'async': false,
'global': false,
'url': "swearWords.json",
'dataType': "json",
'success': function (data) {
words = data;
}
});
return words;
})();
Given the words are in an array you can use jQuery inArray() do this:
var words = ["word1","word2","word3","word4","word5"]
beforeTagAdded: function(event, ui) {
// if ui.tag exists in swearWords.json
// Then output error
var word = "word4"; // I'm assuming you get the word from some control though.
if($.inArray(word , words) > -1){
$('#banned_error').html('You can use this word.')
}
}
DEMO - Checking entered word is in array
Code from DEMO:
<input id="word" type="text"></input>
<button id="checkWord" type="button">Check Word</button>
var words = ["word1","word2","word3","word4","word5"]
$("#checkWord").on("click", function(){
var word = $("#word").val();
if($.inArray(word, words) > -1){
alert("You cannot use this word");
}
});
try this
swearWords.each(function(i,val){
if(ui.tag == val){
$('#banned_error').html("You can't use this word.")
}else{
//do your stuff
}
}
I think, you can use something like this:
beforeTagAdded: function(event, ui) {
if (!this._swearWords) {
// send synchonous XHR to load banned words.
var xhr = null;
if (window.XMLHttpRequest) {
xhr = new window.XMLHttpRequest();
}
else if (window.ActiveXObject) {
xhr = new window.ActiveXObject('Microsoft.XMLHTTP');
}
if (!xhr) {
return;
}
xhr.open('GET', 'swearWords.json', false);
xhr.send(null);
this._swearWords = JSON.parse(xhr.responseText);
}
if (tag in this._swearWords) { // I don't know how to get tag...
// if ui.tag exists in swearWords.json
// Then output error
$('#banned_error').html('You can use this word.')
}
}
It is better and faster to use Object, not Array for definition of banned words.
{'word1': 1, 'word2': 1, 'word3':1}
Then you can use in operator for detection. If you use Array you (or jQuery) have go through array in loop to check existence of the item. Remember that Array.indexOf is not implemented e.g. in IE8.
I am appending solution for cases where you are attaching tagit to class vs. id (as in repeaters i.e search results). When you are attaching to class you have to access tagit within context in order to be able to remove your tag if it fails validation, e.g.
$(".recTags").tagit({
afterTagAdded: function (event, ui) {
if (!ui.duringInitialization) {
// some validation
if (ui.tagLabel.length <= 50) {
$('#tag_error').html('');
// do something
} else {
$('#tag_error').html('Tag "'+ ui.tagLabel +'" is too long [50 character limit].');
ui.tag.parent().tagit("removeTagByLabel", ui.tagLabel);
}
}
}
});
Important piece for me was figuring out how to invoke removeTagByLabel, from which element:
ui.tag.parent().tagit("removeTagByLabel", ui.tagLabel);
I could not use beforeTagAdded event, because ui.tag.parent() does not exist at this point, since tag is not added to the DOM yet, hence check is within afterTagAdded. I am sure there are other ways to do this, but this works for me.
Related
I'm working on a small browser extension (currently targeted for Firefox using the WebExtensions API. The first step was to have it strip ?utm_source=... from a url whenever a new bookmark was added. This works.
function bookmarkCreated(id, bookmarkInfo) {
console.log(`Bookmark ID: ${id}`);
console.log(`Bookmark URL: ${bookmarkInfo.url}`);
currentURL = bookmarkInfo.url;
var strippedURL = currentURL.replace(/\?utm_source=.*/, "");
var newURL = browser.bookmarks.update(id, {
url: strippedURL
});
}
Now I'm working on adding functionality to iterate through all existing bookmarks and strip them of ?utm_source=... This is not working.
I used some example code from MDN that iterates through the bookmarks and outputs the values to the console. This code works fine:
function makeIndent(indentLength) {
return ".".repeat(indentLength);
}
function logItems(bookmarkItem, indent) {
if (bookmarkItem.url) {
console.log(makeIndent(indent) + bookmarkItem.url);
} else {
console.log(makeIndent(indent) + "Folder");
indent++;
}
if (bookmarkItem.children) {
for (child of bookmarkItem.children) {
logItems(child, indent);
}
}
indent--;
}
function logTree(bookmarkItems) {
logItems(bookmarkItems[0], 0);
}
function onRejected(error) {
console.log(`An error: ${error}`);
}
var gettingTree = browser.bookmarks.getTree();
gettingTree.then(logTree, onRejected);`
I added within logItems above a call to bookmarkCreated (first snippet above) - thinking that this should update the url. It seems to pull the bookmarkItem.id fine, but gets the bookmarkItem.url as undefined.
if (bookmarkItem.url) {
console.log(makeIndent(indent) + bookmarkItem.url);
bookmarkCreated(bookmarkItem.id, bookmarkItem.url);
} else {
console.log(makeIndent(indent) + "Folder");
indent++;
}
You are expecting a bookmarkItem as your second paramater, but there is url instead.
Either change signature of bookmarkCreated or change second paramater to bookmarkItem.
I got this to work as expected by hard-coding the class prefix directly into the match():
(function($){
jQuery.fn.extend({
removePrefixClass:function(classPrefix){
$(this).removeClass(function(index, css){
return(css.match(/(^|\s)notice-\S+/g) || []).join(' ');
});
}
});
})(jQuery);
How can I put the function parameter classPrefix inside the match()?
The usage in this case would be $(this).removePrefixClass('notice-') in order to remove any class that is named notice-success, notice-error, notice-warning so on and so forth...
I've seen some answers here where the use of RegExp is suggested. But can't seem to figure it out how to use it in my scenario.
Here is the part of the code where it's in use:
(/* ajax request* / > .done > if there's an existing #notice)
$(function(){
$("form.ajax").submit(function(e){
e.preventDefault();
if($('form[id]').length){ // make sure the form has a unique id
let $form = $('form#'+$(this).attr('id')); // use this specific form
let $formNotice = $form.find('#notice'); // locate #notice element
/* ajax request */
submitAjax($form) // personal wrapper function that just returns an $.ajax({...})-object
.done(function(data,statusText,jqXHR){
if($formNotice.length){ // update existing notice
/*--> */ $formNotice.removePrefixClass('notice-'); // remove class that starts with "notice-"
$formNotice.prependClass('notice-'+data['type']); // add new `notice-`class to the beginning to make it work
$formNotice.find('p').html(data['msg']); // add response message
} else { // set new #notice
$(setNotice(data['type'],data['msg'])).hide().prependTo($form).slideDown("slow"); // personal function that returns the `notice-`-html-element
}
})
.fail(function(){
console.log('fail : '+statusText);
})
.always(function(){
console.log('always');
});
//
} else {
console.log('form is missing id');
}
});
});
You need to build a RegExp object explicitly, like this:
removePrefixClass: function(classPrefix) {
var re = new RegExp('(^|\s)' + classPrefix + '\S+', 'g');
$(this).removeClass(function(index, css) {
return(css.match(re) || []).join(' ');
});
}
I am trying to get the file input preview working.
I have a jquery script which works fine when I call the function normally.
$('#images').on("change", previewImages);
This works.
But when I put the call to the same function differently like following
$('#images').on("change", function(){
previewImages();
});
This doesn't work.
I need to write an if else statement to call a different function on else.
Valid question
Reason: this happens because of this which refers to file element when you are using first approach but in case of second approach this is referring to window element in which it is called. So pass this to function and your question is solved.
$('#images').on("change", function(e) {
/* issue is with this */
previewImages(e, this);
});
var count = 0;
function previewImages(evt, cur) {
var $fileUpload = $("input#images[type='file']");
count = count + parseInt($fileUpload.get(0).files.length);
if (parseInt($fileUpload.get(0).files.length) > 7 || count > 6) {
alert("You can only upload a maximum of 6 files");
count = count - parseInt($fileUpload.get(0).files.length);
evt.preventDefault();
evt.stopPropagation();
return false;
}
$("#taskbar").css("height", "auto");
var $preview = $('#preview').empty();
if (cur.files) $.each(cur.files, readAndPreview);
function readAndPreview(i, file) {
// if (!/\.(jpe?g|png|gif|mp4)$/i.test(file.name)){
// return alert(file.name +" is not an image");
// }
var reader = new FileReader();
$('#preview img').addClass('img-responsive');
$(reader).on("load", function() {
$preview.append($("<img/>", {
src: this.result,
height: 100
}));
});
reader.readAsDataURL(file);
}
}
Create a prePreviewImages function, and use that in your first approach. Inside that function, use the if-statement and call previewImages() or your other function.
The following should do..
$('#images').change(function(e) {
if(e == "some condition"){ // if else goes here
previewImages();
}else {
SomeOtherFun();
}
});
Both ways seem to work for me on the JSFiddle. Are you sure it is not a
browser compatibility issue?
If not are you getting errors logged in the console under developer tools?
I wrote the function below to get the length of a listbox with the id courselessons. The problem is that when I comment the alert() the function changecheckbox works only once.
If I remove the alert it works fine. But I don't want to have an alert on every single click.
The selOpts shows correct content only for the first time.
JavaScript:
function changecheckbox() {
//alert("function called...");
var selOpts = document.getElementById("courselessons").length();
alert(selOpts);
if (selOpts > 0) {
$('#taskassignment').prop('disabled', false);
}
else {
$('#taskassignment').prop('disabled', true).prop("checked", false);
}
}
function addItem() {
if (seltask == undefined || seltask.length === 0) {
return;
}
var lessonsDropdown = $('#courselessons');
lessonsDropdown.empty();
$("#tasks option:selected").appendTo("#coursetasks");
$("#coursetasks option").attr("selected", false);
if (seltask.length > 0) {
cacheToken = new Date().getTime();
// retrieve data using a Url.Action() to construct url
$.getJSON('#Url.Action("AddTaskToCourse")', {
task: seltask,
lesson: lesson,
_: cacheToken
});
$.getJSON('#Url.Action("UpdateLessonInCourse")', {
_: cacheToken
}).done(function (data) {
//re-enable tasks drop down
//for each returned tasks
$.each(data, function (i, lessons) {
//Create new option
var test = $('<option />').html(lessons);
//append tasks taskss drop down
lessonsDropdown.append(test);
});
seltask = null;
});
}
changecheckbox();
}
HTML:
<button type="button" id="btnAdd" onclick="addItem(); changecheckbox();">Add Item</button>
Try using like this,
function changecheckbox() {
//alert("function called...");
var selOpts = $("courselessons").find('option').length;
if (selOpts > 0) {
$('#taskassignment').prop('disabled', false);
}
else {
$('#taskassignment').prop({'disabled':true, 'checked':false});
}
}
or you can do like this ,
$("#btnAdd").click(function(e){
e.preventDefault();
var selOpts = $("#courselessons").find('option').length;
if (selOpts > 0) {
$('#taskassignment').prop('disabled', false);
}
else {
$('#taskassignment').prop({'disabled':true, 'checked':false});
}
});
The code inside addItem() is making a GET request to a resource asynchronously. This means the code which comes after this function to be execute will not wait for its execution to get complete.
When I uncomment the alert it works fine.
That is because as the alert() is built in, it halts the execution of script until user interaction. This gave the time addItem() needs and everything seems to work.
Fortunately, there are solutions available to handle this situation.
Promise.
Rearrange your code to work with callbacks.
Under the covers, $.getJSON is shorthand for making a GET request using ajax with datatype = 'json'and it returns a promise object which basically tells that please wait honey, i will give you something which could be a success or a failure but sometime later.
So yes, you can easily call the function inside the done().
NOTE: These things have been explained pretty well on web so i will not reinvent the wheel :)
Keeping the things simple...
function addItem() {
// Rest of the code...
if (seltask.length > 0) {
cacheToken = new Date().getTime();
// retrieve data using a Url.Action() to construct url
$.getJSON('#Url.Action("AddTaskToCourse")', {
task: seltask,
lesson: lesson,
_: cacheToken
});
$.getJSON('#Url.Action("UpdateLessonInCourse")', {
_: cacheToken
}).done(function (data) {
//re-enable tasks drop down
//for each returned tasks
$.each(data, function (i, lessons) {
//Create new option
var test = $('<option />').html(lessons);
//append tasks taskss drop down
lessonsDropdown.append(test);
});
seltask = null;
changecheckbox();
});
}
}
After this setup, you should remove the changecheckbox() call from the button onclick otherwise it would make it execute twice.
Remove the options, and take the id of select <select id="mySelect"> example:
var selOpts = document.getElementById("mySelect").length;
and your code will be
function changecheckbox() {
//alert("function called...");
var selOpts = document.getElementById("courselessons").length;
if (selOpts > 0) {
$('#taskassignment').prop('disabled', false);
}
else {
$('#taskassignment').prop('disabled', true);
$("#taskassignment").prop("checked", false);
}
}
i get this error, and i don't know how can be solved. I read this link before.
EDIT:1
index.php
<script type="text/javascript">
$(document).ready(function() {
$("#customForm").submit(function() {
var formdata = $("#customForm").serializeArray();
$.ajax({
url: "sent.php",
type: "post",
dataType: "json",
data: formdata,
success: function(data) {
switch (data.livre) {
case 'tags':
$("#msgbox2").fadeTo(200, 0.1, function() {
$(this).html('Empty tags').fadeTo(900, 1);
});
break;
default:
$("#msgbox2").fadeTo(200, 0.1, function() {
$(this).html('Update').fadeTo(900, 1, function() {
$('#conteudo').load('dojo/test_Slider.php');
});
});
break;
}
}
});
return false;
});
});
</script>
test_slider.php
<script type="text/javascript">
var slider = [];
for (i = 0; i < 5; i++) {
slider[i] = (
function(i) {
return function() {
var node = dojo.byId("input"+[i]);
var n = dojo.byId("valores"+[i]);
var rulesNode = document.createElement('div'+[i]);
node.appendChild(rulesNode);
var sliderRules = new dijit.form.HorizontalRule({
count:11,
style:{height:"4px"}
},rulesNode);
var labels = new dijit.form.HorizontalRuleLabels({
style:{height:"1em",fontSize:"75%"},
},n);
var theSlider = new dijit.form.HorizontalSlider({
value:5,
onChange: function(){
console.log(arguments);
},
name:"input"+[i],
onChange:function(val){ dojo.byId('value'+[i]).value = dojo.number.format(1/val,{places:4})},
style:{height:"165px"},
minimum:1,
maximum:9,
}
},node);
theSlider.startup();
sliderRules.startup();
}
})(i);
dojo.addOnLoad(slider[i]);
}
</script>
Problem: First click in submit btn all works well, 5 sliders are imported. Second click, an update is supposed, but i get this message:
Tried to register widget with id==valores0 but that id is already registered
[Demo video]2
Just to add on to #missingo's answer and #Kevin's comment. You could walk through the existing dijits by looking in the registry:
var i = i || 0; // Cache this at the end of your loop
dijit.registry.map(function (widget) {
if (+widget.id.replace(/^[^\d]+/, '') < i) {
widget.destroyRecursive();
}
});
/*
Your loop fixed as described in missingno's answer.
*/
You fell in the age-old trap of making function closures inside a for loop. By the time addOnLoad fires and the sliders are created, i will be equal to 2 and both sliders will try to use the same DOM nodes (something that is not allowed).
You need to make sure that you give a fresh copy of i for everyone. The following is a quick fix:
for(i=0; i<2; i++){
(function(i){
slider[i] = ...
//everything inside here remains the same
//except that they now use their own i from the wrapper function
//instead of sharing the i from outside.
}(i));
}
Dijit stores all active widgets in the dijit.registry, and uses id's as unique qualifiers. You can't create dijits with same id.
Need to clean dojo.registry before create a new slider dijits. Add this code before declare dijit on test_slider.php
dijit.registry["input"+ [i]].destroyRecursive();
can you assign any number ID like ID generated by 10 digit random number or something with datetime combination so id will never be same.