I have been working with this script and its mind boggling as it looks as though it should work correctly, however it is not. So I turn to you all for an extra set of eyes on what I am missing.
Situation: Basically, what I am trying to do is on click detach a div, then when another radio button is selected, append the div back to its position.
I have included a JSFiddle so you can see that I may not be entirely off track : http://jsfiddle.net/heykate/pusk6ezx/
On load, the user is presented with two options, Support or Inflatable with the default size selected on the support size with the other models sizes hidden. Which is fine. I know ultimately I want to change that first line of code from hide() to detach().
Once I go to click on the second style option, it shows the second models widths like it is supposed to, however if I was to switch back to the first style option. The div I originally detached, is still hidden and will not .append() within my code.
Here is what my script looks like:
$(document).ready(function(){
$('#5e8a1520d82ed2834919fda63f4a3f84').hide();
$('input[type="radio"]').change(function(){
if($(this).attr("value")=="489"){
$( "#b6304c97422f08727702021e2c6c7cda" ).append( ".rightCol" );
$("#5e8a1520d82ed2834919fda63f4a3f84").detach();
}
if($(this).attr("value")=="488"){
$("#b6304c97422f08727702021e2c6c7cda").detach();
$("#5e8a1520d82ed2834919fda63f4a3f84").show();
}
});
});
Please let me know what I am missing. Basically the reason I am doing this to begin with is
To make it less confusing on the front end and
because the defaults are set (for both sizes) they get mashed up in the product order info, so if I could clear the other widths checked radio button or detach and prevent it from being seen, I think it would work out just fine.
.detach REMOVES the element from the DOM. Since it's no longer in the DOM, when you try to .show() it later, it's no longer in the dom, and therefore not findable/usable. That's why .detach() returns the node you've removed, in case you want to re-use it:
foo = $('#blahblah').detach(); // 'foo' now contains the detached now
$('#otherplace').attach(foo); // reinsert the foo element we detached.
//OR
$('#otherplace').append(foo);
Related
I have a function that dynamically creates div elements based upon whatever input is given, and lets them choose certain items by clicking on each div. I have it so that if the div is clicked, a function (named checkToggle) is called that makes it looks like it is selected and adjusts some related variables. There is a checkbox in the div element that is toggled by this function (hence its name). Long story short, I had to jump through some hoops to get it to work, most of which I don't even remember. Please don't ask me about that.
The point of this question is this. I initially used the following JavaScript code to run the function when the checkbox was clicked. It was assigned by the main function, which created these div elements using a for loop.
document.getElementById(`${itemID}-checkbox`).onclick = function() {
checkToggle(`${itemID}-checkbox`);
};
This works, but I wanted to try to convert all of my onClick functions to JQuery. Here is the JQuery alternative I created.
$(`${itemID}-checkbox`).on(`click`, function() {
checkToggle(`${itemID}-checkbox`);
});
While the code itself seems to be fine, it does not work. It seems as if JQuery functions cannot be created like this in a for loop or something. It is applied after the element is created and put in its place, so I don't think it has anything to do with the element not being ready. I am also having the same issue with 2 other similar cases. Any idea as of why this isn't working?
Let me know if more information is needed and if so, what kind of information is needed.
You need to update the selector to Target HTML id using the # character. Simply prepend the character to the query:
$(`#${itemID}-checkbox`).on(`click`, function() { checkToggle(`${itemID}-checkbox`); });
It would also apply to DOM methods querySelector or querySelectorAll as well.
Hopefully that helps!
I have a dropdown, and a list of elements that default to display:none with css.
Currently, when an element is selected from the dropdown it's changed to display:block
What I'm missing, is how to change the element back to display:none once a new one is selected. I know I could write a loop to constantly check every element and change it to display:none but that seems cumbersome.
My real problem has about 100 elements, and it seems wasteful to re-hide all of them when 99 of them will already be hidden.
Curious what the most elegant way to do this in jQuery (or javascript) is. A fiddle of what I have is here:
https://jsfiddle.net/3w66k51z/4/
Thanks!
I've added $(".sReport").hide(); before your .show() call in order to hide all of the elements.
jQuery.hide() will set the element's display to none
jsfiddle
You could add state so that you know the currently shown item (if any) and hide just that one element. I understand you don't want to hide all items when most of them will already be hidden.
Here is a simple implementation of this idea.
(I imagine your real problem involves many more hidden items than the fiddle. If not — maybe even if so — this may be a premature optimization, and there’s nothing wrong with calling .hide() or whatever on all items.)
Create array of your element then create a function to hide all elements but not the one you want to be display.
Here's an example http://jsbin.com/ukoqud/3/edit
If you click on a red box, you'll get an alert.
If you click on a link, everything in a blue box will be replaced with just a red box. Link will disappear and if you click on a red box then, you'll get no alert.
Why this happens?
Is it related to innerHTML?
Does it work the same way in all browsers?
Here's one more example http://jsbin.com/ukoqud/1/edit In this one you'll get an alert after clicking on a link. Things happen in a quite similar way, but result is different.
I would like to understand the reason, there's no need to fix my code.
When you call $(".red"), it returns a collection of DOM elements that exist at that moment. So $(".red").click(function...) just binds a handler to the click event on those elements. If you later create new elements with the same class, they weren't in this collection, so they don't have the handler bound to them. jQuery doesn't watch the DOM for changes and update the handlers dynamically -- the bindings are just on the elements you matched at the time you called click().
You either need to bind the handler again after adding the new HTML, or use delegation with .on():
$(".blue").on("click", ".red", function(){
alert('click on a red box detected');
});
This works by binding a handler to $(".blue"), which doesn't change dynamically. The handler checks whether the element you clicked on matches the ".red" selector, so it's able to handle dynamically-added elements without requiring rebinding.
I think the reason why it works in your second example is because the red block isn't inside the blue box to start. When you move it inside, jQuery reuses the same DOM elements, so the bindings go along with it. In the first example, the red box starts out inside the blue box. When you do $('.red').parent().html(...), the first thing it does is empty $('.red').parent() (the blue box), so the original red element is removed from the DOM, and its bindings are lost.
We need to understand how setting html of an element works. Then you will figure out your answer yourself.
Take a look at this bin Updated Bin
When we set HTML of an element, it first removes all the elements inside it.
Those elements are not removed from memory depending upon whether they are garbage collected or not.
If any of the child is having a reference, then that particular child won't be garbage collected.
In your case, we are having a reference to red element so it is still present in memory but not a part of document.
When we say blue.html(red) in my example, red element becomes a part of document again but this time there won't be any handlers on it So your click does not work.
While in your example2,
red element is always a part of document hence no handlers were lost when red element is moved inside blue element.
I hope this will help.
because when u click the link, you delete everything on screen and create everything from a scratch and event binding goes away. so you should use this
$(".blue").on("click", ".red", function(){
alert('');
});
this way, binding is done differently. it doesnt bind it statically
When a draggable attribute is enabled on a parent element(<li>) I cant make contenteditable work on its child element (<a>).
The focus goes on to child element (<a>),but I cant edit it at all.
Please check this sample
http://jsfiddle.net/pavank/4rdpV/11/
EDIT: I can edit content when I disable draggable on <li>
I came across the same problem today, and found a solution [using jQuery]
$('body').delegate('[contenteditable=true]','focus',function(){
$(this).parents('[draggable=true]')
.attr('data-draggableDisabled',1)
.removeAttr('draggable');
$(this).blur(function(){
$(this).parents('[data-draggableDisabled="1"]')
.attr('draggable','true')
.removeAttr('data-draggableDisabled');
});
});
$('body') can be replaced by anything more specific.
If new contenteditable elements are not added in the runtime, one can use bind instead of delegate.
It makes sense that the draggable and contenteditable properties would collide. contenteditable elements, like any text field, will focus on mousedown (not click). draggable elements operate based on mousemove, but so does selecting text in a contenteditable element, so how would the browser determine whether you are trying to drag the element or select text? Since the properties can't coexist on the same element, it appears that you need a javascript solution.
Try adding these two attributes to your anchor tag:
onfocus="this.parentNode.draggable = false;"
onblur="this.parentNode.draggable = true;"
That works for me if I add it to the <a> tags in your jsFiddle. You could also use jQuery if it's more complicated than getting the parentNode.
Note: This is a workaround since I believe the inability for these two functionalities to work together resides in the HTML spec itself (i.e. the not working together thing is intentional since the browser can't determine whether you want to focus or drag on the mousedown event)
I noticed you explicitly set 'no libraries', so I will provide a raw javascript/HTML5 answer
http://jsfiddle.net/4rdpV/26/
This was my crack at it.
First of all, it might be better to include the data in one single localStorage item, rather than scatter it.
storage={
'1.text':'test 1',
'2.text':'test 2'
}
if(localStorage['test']){
storage=JSON.parse(localStorage['test'])
}
this creates that ability, using JSON to convert between object and string. Objects can indeed be nested
I also added (edit) links next to the items, when clicked, these links will transform the items into input elements, so you can edit the text. After hitting enter, it transforms it back and saves the data. At the same time, the list items remain draggable.
After saving, hit F12 in chrome, find the console, and look in the localStorage object, you will see all the data was saved in localStorage['test'] as an Object using JSON.stringify()
I tried my best to design this to be scaleable, and I think I succeeded well enough; you just need to replace the HTML with a container and use a javascript for loop to write out several items, using the iterator of your choice to fill the parameter for edit(). For example:
Say you changed storage to hold "paradigms" of lists, and you have one called "shopping list". And say the storage object looks something like this:
{
"shopping list":{
1:"Milk",
2:"Eggs",
3:"Bread"
}
}
This could render that list out:
for(i in storage['shopping list']){
_item = storage['shopping list'][i];
container.innerHTML+='<li draggable=true><a id="item'+i+'">'+_item+'</a> (edit)</li>'
}
Of course, if you were to edit the structure of the storage object, you would need to edit the functions as well.
The output would look something like this:
Milk (edit)
Eggs (edit)
Bread (edit)
Don't worry about the input elements if that worries you; CSS can easily fix it to look like it didn't just change.
If you don't want the (edit) links to be visible, for example, you can do this in CSS:
a[href="#"]{
display:none;
}
li[draggable="true"]:hover a[href="#"]{
display:inline;
}
Now the edit links will only appear when you hover the mouse over the list item, like this version:
http://jsfiddle.net/4rdpV/27/
I hope this answer helped.
Using html5sortable and newer JQuery events (delegate is deprecated, answer 3 years after initial question), bug still affects Chrome 37. Contenteditable spans and html5sortable seem to play nice in other browsers. I know this is only partially relevant, just keeping documentation on changes I've noticed.
$(document).on('focus', 'li span[contenteditable]', function() {
$(this).parent().parent().sortable('destroy'); // removes sortable from the whole parent UL
});
$(document).on('blur', 'li span[contenteditable]', function() {
$(this).parent().parent().sortable({ connectWith: '.sortable' }); // re-adds sortable to the parent UL
});
I'm creating a web-app interface which will allow users to construct sentences by clicking on words/phrases. Each word of phrase will be contained in its own page element, eg. a <div>. So the div class would need to allow:
dragging into a different order relative to the other divs/words (ie. sorting)
an X (only visible when hovering over) in the top right corner of its box to remove it completely upon clicking
changing of font/text upon click actions
be inserted into the page when the user wishes to add another word
For instance, imagine this is the page (quote marks denote an element):
"Hi, my name is" [Textbox] "I work at..." "and I was born in" [Combobox]
The phrase in italics is 'inactive' and not part of the sentence, but if the user clicked it then it would change to "I work at" [Textbox] and a new div would be added eg. "And also at..." to allow further expansion. The phrases in bold would have a close button /clickable action to get rid of them or make them inactive again. Also they would be draggable to change the order. As you can see I would also need to dynamically insert new textboxes and comboboxes to accommodate more phrases requiring input.
Could somebody give me a brief run down on what steps I need to take? I've seen sortable list elements in JQuery eg. http://jsfiddle.net/ctrlfrk/A4K4t/ which is a start. Would I just need to spruce this up with some basic JavaScript and CSS? Or would I need to use server-side scripting to dynamically add more page content?
To summarize, I need the div to change font/text upon clicking, have a hover-over close button, remove and be inserted upon simple click events, and be sortable by dragging. I also need comboboxes/textboxes to be added/removed in parallel.
Any tips would be greatly appreciated.
You don't need the server for new content unless it is stored or generated there.
Not having done this exact thing before I don't see any grand scheme to implement, I would just add the features incrementally until you get where you need to go. You may have to refactor things a bit along the way, but I probably don't have to tell you that. ;)
One thing you will have to do when you add or delete elements is to rebind the jQuery functions, it won't enough to just call them when the document loads. So put them in a function and call it whenever you add or remove an element.
Regarding your description of the UI behavior, you say that inactive elements can be dragged and that they can be made active by clicking on them. You can do this (just see if the thing has been dragged or not on mouseup to know whether it was drag or a click) but it might not be the best UI design choice, IMO. It wouldn't be the worst thing in the world, but I find it a little frustrating when the wrong thing happens when I try to do something. Of course, implementing and seeing for yourself is probably best.
Sorting:
I would implement the phrases in an unordered list (ul) which makes it as simple as
$("#ulId").sortable();
You can make ul/li items stack next to each other in a similar manner to how you make horizontal navigation menus out of ul elements.
A destrunction button:
Just use a template li similar to this
<li id="text1Wrapper">
<span id="text1"
onmouseover="javascript:$('text1Remove').fadeIn();"
onmouseout="javascript:$('text1Remove').fadeOut();"
onclick="javascript:$('text1Content').
replaceWith(
$(document.createElement('input'))
.attr('type','text')
.val($('text1Content').text())
);">
<span id="text1Content">Text Here</span>
<img id="text1Remove"
style="display: none;"
src="./x.jpg" alt="Remove"
onclick="javascript:$(this).parent().remove();"
/>
</span>
</li>
A quick description:
The span mouse over event tells jquery to fade in the remove button (so when you hover, the remove button becomes available).
The span mouse out event tells jquery to fade out the remove button when it's no longer needed (so when you move the mouse off the li, the remove button is no longer visible)
The onclick of the span replaces the span with a text box containing the content of the span. I will leave the "save changes" as an excercise for the reader, since it's essentially the same but in reverse.
Img tag style has display: none to hide it initially. This is essentially the end product of a fadeOut() but without the fade.
The on click event of the remove button gets the parent (the li) and removes it, and all children from the dom.
Note that the events are only put here because it seemed the logical place to explain it. All events in the outer SPAN tag are a useless waste of space, as they will all be overridden when we clone the node in the next section.
Insertion into the page:
All you have to do now is
var cloneLi = $('#text1Wrapper').clone();
cloneLi.attr('id', 'text2Wrapper');
var cloneSpan = cloneLi.children('#text1').attr('id', 'text2');
var cloneContent = cloneSpan.children('#text1Content').attr('id', 'text2Content');
var cloneRemove = cloneSpan.children('#text1Remove').attr('id', 'text2Remove');
You will then need to change the mouseover, mouseout and onclick functions of the outer span using jquery events:
cloneSpan.mouseover(function(e) {
// Insert functionality from template here
});
cloneSpan.mouseout(function(e) {
// Insert functionality from template here
});
cloneSpan.click(function(e) {
// Insert functionality from template here
});