1. Advertising
    y u no do it?

    Advertising (learn more)

    Advertise virtually anything here, with CPM banner ads, CPM email ads and CPC contextual links. You can target relevant areas of the site and show ads based on geographical location of the user if you wish.

    Starts at just $1 per CPM or $0.10 per CPC.

Matching element with content, and if not found, add it

Discussion in 'jQuery' started by PoPSiCLe, Jan 13, 2016.

  1. #1
    Okay. I have two unordered lists. The list elements in each list is either active, or deactivated (one list for active elements, and one for deactivated). The user who has access can click on a deactivae/reactivate button in each list element, and move the element to the other list. All this works, no problem.

    However, I'm abusing the list a bit, and creating certain list elements with the class "heading" - these are assigned a value, of which there are corresponding list elements that can be activated or deactivated. What I want is a way to copy the closest list-element with the heading class to the other unordered list, if that heading isn't already present in the list.

    Confused yet? Let me try to show what I mean:

    Active elements:
    
    <ul id="activelements">
      <li class="heading">This is a heading</li>
      <li>This is an active element</li>
      <li>This is another active element</li>
      <li class="heading">This is another heading</li>
      <li>This is yet another active element</li>
    </ul>
    
    Code (markup):
    Inactive elements:
    
    <ul id="inactivelements">
    </ul>
    
    Code (markup):
    No elements here, since none have been deactivated yet

    If I deactivate the second element under the first heading in #activelements, I want the "This is a heading"-heading to come with, and be inserted before the list-element, in the deactivated elements list. This works.

    However, if I deactivate the other element under "This is a heading", I don't want that heading to be part of the list, since it's already present - and that's what I'm struggling with.

    Any tips of how I can match the closest heading-element from the list-element I've chosen to activate / deactivate, with the content, to an element in the list I'm moving it to? Note: there might be a lot of different heading-elements, all with different content - so I must match every one, and if any one of them match, I need to NOT add the heading - if none of them match, I must add the heading (and of course place the moved element under the correct heading, but that should be fairly easy to accomplish).

    I have some jQuery code for this already, of course, but was pondering if anyone could provide me with something elegant and short - the current code I have is a bit bloated.
     
    Solved! View solution.
    PoPSiCLe, Jan 13, 2016 IP
  2. deathshadow

    deathshadow Acclaimed Member

    Messages:
    9,732
    Likes Received:
    1,998
    Best Answers:
    253
    Trophy Points:
    515
    #2
    A bit bloated you say? In jQuery? REALLY? Who saw that coming?

    Remember how I'm always saying "make it work without scripting FIRST!"? Yeah, that.

    Sounds like what you want is a checkbox and a label, not two columns. The "trick" would be convincing the scripttard version to make a copy of those labels that are only shown when their corresponding input is selected.

    For markup, have something like:

    <ul id="selectList">
      <li>
    		<input type="checkbox" id="element1" checked>
    		<label for="element1">Describe This</label>
    	</li><li>
    		<input type="checkbox" id="element2" checked>
    		<label for="element2">Describe This</label>
    	</li><li>
    		<input type="checkbox" id="element3">
    		<label for="element3">Describe This</label>
    	</li><li>
    		<input type="checkbox" id="element4" checked>
    		<label for="element4">Describe This</label>
    	</li>
    </ul>
    Code (markup):
    Where checked means you want it in "selectList"

    Works scripting on or off as to activated/not activated. When scripting comes on I'd create a new empty list appended after the first, iterate through all the LI via nodeWalking, attaching to each input a onclick AND onchange handler (sucks but you need to attach the same handler to both as IE and FF are stupid), then cloning the label into a new list item in that second list, attaching BOTH the labels as properties of the input (using data- attributes actually works nice for this) so they can have a class swap to display or not display them.

    Remember, clicking on the label is the same as clicking on a checkbox, so just hide the checkbox when scripting is on. (one of the few times I'd attach a class to BODY).

    Gimme a few and I'll toss together a demo, though excuse the lack of jQuery I'm not STUPID ENOUGH to use that crap.
     
    deathshadow, Jan 13, 2016 IP
  3. deathshadow

    deathshadow Acclaimed Member

    Messages:
    9,732
    Likes Received:
    1,998
    Best Answers:
    253
    Trophy Points:
    515
    #3
    Here's a working demo:

    http://www.cutcodedown.com/for_others/PoPSiCLe/listSelect/template.html

    The markup for the actual functionality:
    	<div id="scriptMessage"></div>
    	<noscript>
    		<p>
    			Column functionality require JavaScript, but you can still select or deselect items by using the checkboxes
    		</p>
    	</noscript>
    
    	<ul id="selected">
    		<li>
    			<input type="checkbox" id="element1" checked>
    			<label for="element1">First</label>
    		</li><li>
    			<input type="checkbox" id="element2" checked>
    			<label for="element2">Second</label>
    		</li><li>
    			<input type="checkbox" id="element3">
    			<label for="element3">Third</label>
    		</li><li>
    			<input type="checkbox" id="element4" checked>
    			<label for="element4">Fourth</label>
    		</li>
    	</ul>
    Code (markup):
    Is pretty simple. If you don't want any LI to be copyable, I made the script check for the presence of an input, so if you want your headings in there just don't put a checkbox or label in there.

    The script:
    http://www.cutcodedown.com/for_others/PoPSiCLe/listSelect/listSelect.js

    Starts out with some of my helper functions since jQ just pisses me off as too big, improper/sloppy methodologies and trying to hard to change how JavaScript works.

    The actual functionality is pretty simple. I have a different message added scripting on, remembering that if it's only useful for scripting it probably shouldn't be in the markup apart from perhaps an empty hook. I toss a class on body to say "yes scripting is running" so the style can be applied different.

    I get hold of the selected list by it's ID, grab the first LI, and create the non-selected UL -- since again there's no reason for that list to exist scripting off. I append this new list after the UL#selected.

    I then nodewalk through the LI (using handy helpers to filter out all non-element nodes) and it's children to hook the inputs, create new LI in the new list, put the existing LI and new LI onto the checkbox element as data_ properties, call the status update function to add or remove display:hidden as appropriate, and then hook each checkbox for both onclick and onchange (since not all browsers do that in the same order :( ) to call the update hidden/block on the LI as appropriate.

    Not too complex. I also dumped the whole thing in a self starting function so these extra functions and variables don't pollute the global namespace.

    You COULD also have the handler just copy the values from one list to the other, but that amount of DOM manipulation in realtime can chew on memory from a lack of quality garbage collection in FF if someone clicks back and forth between the two columns too much. It also makes it a lot easier to keep them in order since they all exist all the time in the proper order, we're just swapping how they are displayed.

    Again, what column they are displayed in actually means two things; jack and ****, since the state of the checkbox is what's actually being changed -- which I would think makes it easier to send that data server-side particularly if this is going to be in a form.

    Another approach would need CSS3 to function, but you could just the width of the UL to twice the LI width, and simply use position:relative with left:0 and left:50% to slide them over to the other side -- though that would NOT compress them vertically which I believe is your intent.
     
    deathshadow, Jan 13, 2016 IP
  4. PoPSiCLe

    PoPSiCLe Illustrious Member

    Messages:
    4,623
    Likes Received:
    725
    Best Answers:
    152
    Trophy Points:
    470
    #4
    Well, yes, that is actually a lot neater, but doesn't address my actual problem - the "heading" inside the list. Ie, I have a separate element (well, another li-element) that has nothing to do with the movable items, but is a heading (yes, I know it's list-abuse) - I want to move the heading, when needed, but not if the heading already exist in the markup for the list I'm moving the item to.

    This is basically a list of daily events, that can be either active (used in a in-house calendar) or inactive (not used at all) - the active ones, if clicked to deactivate, goes to the deactivated list, and vice versa. However, it's possible to add and/or delete these items alltogether. Hence, the same location can have multiple events for the same day, either active or inactive, or for different days, or a combination of all of those. Hence, I need to be able to ALSO move the heading-li-element when I'm moving the first item belonging to that heading. Which my code currently does, but it needs to check to see if that heading is already present, so as to avoid double up of the same heading. Hence, I was looking for something that walks the DOM, picks the closest heading, and copies that to the other list _if that heading is not already present_. If the heading is present, it will just copy the element to the other list, not bothering with the heading at all.
     
    PoPSiCLe, Jan 13, 2016 IP
  5. deathshadow

    deathshadow Acclaimed Member

    Messages:
    9,732
    Likes Received:
    1,998
    Best Answers:
    253
    Trophy Points:
    515
    #5
    Ok, I can't even make sense out of what you just said. Alright, what is the DATA and what is the POINT of this? You're describing functionality that makes zero sense to me to even HAVE that functionality -- so I think I need to know more about WHAT you are manipulating and why.

    I would think if you were "moving" the heading you'd be moving ALL it's children, not just the first child... in which case I'd be building lists inside lists -- the "heading" being in the parent LI just like a dropdown menu.

    Though if these are ACTUALLY headings, I'd have them as heading tags and have both exist all the time in both lists, hiding them only when they have no children.

    Really though, that starts to sound like something I'd never put on a website in the first place -- or an applet for that matter.
     
    deathshadow, Jan 13, 2016 IP
  6. PoPSiCLe

    PoPSiCLe Illustrious Member

    Messages:
    4,623
    Likes Received:
    725
    Best Answers:
    152
    Trophy Points:
    470
    #6
    Basically, what you have is something like this:
    
    <ul id="active_events">
      <li class="heading">Location 1</li>
      <li>Event 1 - day 1</li>
      <li>Event 2 - day 2</li>
      <li class="heading">Location 2</li>
      <li>Event 3 - day 7</li>
      <li>Event 4 - day 5</li>
    </ul>
    
    <ul id="inactive_events">
      <li class="heading">Location 3</li>
      <li>Event 5 - deactivated event - day 4</li>
    </ul>
    
    Code (markup):
    Okay - if I now go ahead and deactivate Event 2, it would move to the inactive_events list. But, the heading (Location 1) wouldn't be in the second list - hence it would need to be copied as well. That isn't a problem. However, if I'm gonna deactivate Event 1 as well, after deactivating Event 2, the heading for those events (Location 1) would already be in the inactive_events list - and wouldn't need to be copied over again. Hence I was looking for a simple way to check to see if the heading for the element I was currently manipulating was already in the receiving list, and if not, copy it over.

    However, I'm pondering if I should just populate both lists with the locations, and basically just hide/show them based on having events in them. Won't look as good, but will make things a bit easier to work with.
     
    PoPSiCLe, Jan 13, 2016 IP
  7. PoPSiCLe

    PoPSiCLe Illustrious Member

    Messages:
    4,623
    Likes Received:
    725
    Best Answers:
    152
    Trophy Points:
    470
    #7
    Oh, well - I decided to redo the whole thing, and make it simpler (always a good thing). One <ul> with the elements listed, and then just change the color of the deactivated ones (set them as red or orange or something like that) - makes the whole page shorter, less code, and no need for complicated structures or scripting, just a simple class-swap and a couple of attributes that needs to be changed.
     
    PoPSiCLe, Jan 14, 2016 IP
  8. #8
    I just took another stab at it as I THINK I got what you are trying to do... I switched up the markup a bit:

    	<div id="selected">
    		<h2>Active Events</h2>
    
    		<div class="subSection">
    			<h3>Location 1</h3>
    			<ul>
    				<li>
    					<input type="checkbox" id="event1Loc1" checked>
    					<label for="event1Loc1">First Event</label>
    				</li><li>
    					<input type="checkbox" id="event2Loc1" checked>
    					<label for="event2Loc1">Second Event</label>
    				</li><li>
    					<input type="checkbox" id="event3Loc1">
    					<label for="event3Loc1">Third Event</label>
    				</li><li>
    					<input type="checkbox" id="event4Loc1" checked>
    					<label for="event4Loc1">Fourth Event</label>
    				</li>
    			</ul>
    		<!-- .subSection --></div>
    
    		<div class="subSection">
    			<h3>Location 2</h3>
    			<ul>
    				<li>
    					<input type="checkbox" id="event1Loc2" checked>
    					<label for="event1Loc2">First Event</label>
    				</li><li>
    					<input type="checkbox" id="event2Loc2" checked>
    					<label for="event2Loc2">Second Event</label>
    				</li>
    			</ul>
    		<!-- .subSection --></div>
    
    		<div class="subSection">
    			<h3>Location 3</h3>
    			<ul>
    				<li>
    					<input type="checkbox" id="event1Loc3" checked>
    					<label for="event1Loc3">First Event</label>
    				</li><li>
    					<input type="checkbox" id="event2Loc3" checked>
    					<label for="event2Loc3">Second Event</label>
    				</li>
    			</ul>
    		<!-- .subSection --></div>
    
    	<!-- #selected --></div>
    Code (markup):
    Using proper headings and lists. Whenever there's a change I go to that checkbox's parent DIV, look at all the checkboxes inside it and set the appropriate #selected>div and #nonSelected>div style.display property as appropriate.

    As with all my examples the directory is wide open fer ye.
    http://www.cutcodedown.com/for_others/PoPSiCLe/listSelect2/

    Lets you move the entire group, or single entries from the group. If the group has no entries in it's column, the entire DIV (and therein the heading) are hidden.

    Is that what you were thinking?
     
    deathshadow, Jan 14, 2016 IP
    PoPSiCLe likes this.
  9. PoPSiCLe

    PoPSiCLe Illustrious Member

    Messages:
    4,623
    Likes Received:
    725
    Best Answers:
    152
    Trophy Points:
    470
    #9
    Hey, that's neat. Quite a LOT of javascript, though - although some of it is of course helper-functions loaded here for the purpose of getting the actual functionality to work. Looks neat, works perfectly. I might use this (however, considering my previous post, I will have to look into maybe just using one list and just mark elements active or inactive). Thanks for the effort, man.
     
    PoPSiCLe, Jan 14, 2016 IP