Drag and drop surprise: When items are dragged onto the screen, a magical box will appear. But watch as the box disappears when the item is dragged

I am a newcomer to knockout JavaScript and am currently utilizing Knockout drag and drop functionality in my project. Initially, I have two divisions - one is visible while the other has a display property set to none. During the drag enter function, I want to hide the first division and display the second division. However, upon dragging leaving, I need to hide the second division and show the first division again. The issue arises when the functionality of the second division changes during drag enter, but I do not wish to hide the second division until the drop event occurs. Any assistance in resolving this matter would be greatly appreciated.

It is important that no HTML is altered within the ".typeTextareaSection" block in the code.

You can view my fiddle Knockout Drag and Drop Fiddle

/** JavaScript ViewModel Code **/
 /* ViewModel Function */
function ViewModel(){
    var self = this;
    this.dropZones = ko.observableArray([{
        'elements' : ko.observableArray([])  // just for showcasing purposes
    }]);

   
    this.dragoverTextarea = function(e){
        console.log('dragOver');
        e.stopPropagation();
        e.preventDefault();
    }

    this.dropTextarea = function(e, data){
        console.log('drop');
        e.stopPropagation();
        e.preventDefault();
        var files = e.dataTransfer.files;
        for (var i = 0, f; f = files[i]; i++) {
            data.elements.push(f.name);
        }
        $('.typeTextarea').children('.typeTextareaSection').css('display', 'block');
$('.typeTextarea').children('#dragtimeTextarea').css('display', 'none');

    }

    this.dragenterTextarea = function(e, index){
        console.log('dragEnter');
              $('.typeTextarea').eq(index).children('.typeTextareaSection').css('display', 'none');
$('.typeTextarea').children('#dragtimeTextarea').css('display', 'block');
    }

    this.dragleaveTextarea = function(e, index){
        console.log('end');        
        $('.typeTextarea').children('.typeTextareaSection').css('display', 'block');
$('.typeTextarea').children('#dragtimeTextarea').css('display', 'none');
    }
}

// Applying Knockout bindings to the ViewModel
ko.applyBindings(new ViewModel());
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/2.3.0/knockout-min.js"></script>

/*HTML Markup*/
<div class="col-md-12" data-bind="foreach: dropZones">
    <div class="typeTextarea" style="margin-top: 20px; height: 120px; border: 2px dashed #bbb; padding: 10px;" data-bind="event: {
        dragover: function (data, e) { $root.dragoverTextarea(e); },
        drop: function (data, e) { $root.dropTextarea(e, $data); },
        dragenter: function (data, e) { $root.dragenterTextarea(e, $index()); },
        dragleave: function (data, e) { $root.dragleaveTextarea(e, $index()); }
    }">
        /* Content of Type Textarea Division */
    </div>
</div>

Answer №1

Your problem-solving strategy lacks structure.

The key concept to remember is that any time you make changes to the DOM from your viewmodel, it's a mistake. Your viewmodel should not be aware of the page layout, elements, CSS classes, or DOM events. The view should rely on the viewmodel, not vice versa.

The only appropriate place for DOM manipulation is within a binding. If there isn't a pre-existing binding that meets your needs, you can always create a custom one.

In this scenario, you want to encapsulate the action of dropping files onto an element. This action should have two outcomes:

  • Certain elements should display a reaction when hovered over
  • The dropped files should be placed into an observable array

These requirements define the interface of your binding:

  • An observable that stores either true or false based on hovering status
  • An observable to receive the dropped files

To achieve this, let's name the new binding filedrop, and here's how you would use it:

<div data-bind="filedrop: {hover: hovering, drop: files}"></div>

Where hovering and files are observables in your viewmodel. Here's how the binding could be implemented:

ko.bindingHandlers.filedrop = {
    init: function (element, valueAccessor) {
        var options = valueAccessor();

        ko.utils.registerEventHandler(element, "dragenter", function (e) {
            if (ko.isWriteableObservable(options.hover)) options.hover(true);
            e.preventDefault();
        });
        ko.utils.registerEventHandler(element, "dragleave", function (e) {
            if (ko.isWriteableObservable(options.hover)) options.hover(false);
            e.preventDefault();
        });
        ko.utils.registerEventHandler(element, "dragover", function (e) {
            e.preventDefault();
        });
        ko.utils.registerEventHandler(element, "drop", function (e) {
            if (ko.isWriteableObservable(options.drop)) {
                if (typeof options.drop.push === "function") {
                    options.drop.push.apply(options.drop, e.dataTransfer.files);
                } else {
                    options.drop(e.dataTransfer.files);
                }
            }
            if (ko.isWriteableObservable(options.hover)) options.hover(false);
            e.preventDefault();
        });
    }
};

You can simplify your viewmodel by creating a dedicated DropZone viewmodel containing the hovering and files observables as well as other related attributes:

function DropZone() {
    var self = this;
    self.text = ko.observable();
    self.files = ko.observableArray();
    self.hovering = ko.observable(false);
    self.filenames = ko.computed(function () {
        return ko.utils.arrayMap(self.files(), function (element) {
            return element.name;
        });
    });
}

Now your main view model accurately reflects a list of drop zones:

function ViewModel() {
    var self = this;
    self.dropZones = ko.observableArray([
        new DropZone()
    ]);
}

From here, constructing the view becomes straightforward:

<div class="col-md-12" data-bind="foreach: dropZones">
    <div class="dropZone" data-bind="filedrop: { hover: hovering, drop: files }">
        <div class="textPane" data-bind="visible: !hovering()">
            <div class="buttonBar">
                <img src="Content/images/cancel27.png" alt="Cancel" title="Cancel" />
                <img src="Content/images/correctBox2.png" alt="Save" title="Save" />
            </div>
            <textarea maxlength="25000" data-bind="value: text" placeholder="Start typing here&hellip;"></textarea>
        </div>
        <div class="dropPane" data-bind="visible: hovering">Drop files here&hellip;</div>
    </div>
</div>

This approach results in:

  • A reusable custom binding
  • Viewmodels that only contain relevant data
  • A clean and sustainable setup overall

http://jsfiddle.net/kd7umbj4/2/

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Navigating a Bootstrap carousel with buttons: A step-by-step guide

Here is the code snippet I am currently working with: <!DOCTYPE html> <html> <head> <script src="https://code.jquery.com/jquery-2.1.4.js"></script> <link href="https://code.jquery.com/ui/1.11.4/them ...

Retrieve a specific element using BeautifulSoup

Looking at this html structure found in a bs4.BeautifulSoup: <div class="bordered radius border-color-ui-2 bg-white h-100"> <a href="#" class="box-card-link-wishlist" role="button" aria-label="Adicion ...

Including a CSS link in a .css file is an essential step in styling

Can someone guide me on how to reference the Bootstrap link (https://maxcdn.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css) in a .css file within an Angular project? ...

Insert the current row into the table

I'm working with an HTML table structure that looks like this: <table id="test"> <thead> <tr> <td></td> <td></td> <td></td> <td></td> ...

Display partials with ajax in Rails 3/jquery tabs

I am currently working on setting up a simple 3 tab navigation for my post#show page using Rails 3 and jquery. The goal is to render _post, _comments, and _related partials into the view when each tab is clicked. Currently, these partials do not have thei ...

Tips for designing a tabbed interface using HTML

I am attempting to design a tabbed interface in HTML on my Google Sites page. I want it to behave similarly to this example: http://www.w3schools.com/bootstrap/tryit.asp?filename=trybs_pills_dynamic&stacked=h However, when I insert the code into an HT ...

Retrieve RadioButtonList Value using jQuery Function

Here is the code snippet I am working with: <tr> <td> <span>Random question here?</span> </td> <td> <asp:RadioButtonList ID="someList" runat="server" SelectedValue="<%# Bind('someValue') %>" R ...

Developing a HTML button through Javascript that triggers a method with a single parameter when clicked

My current project involves creating HTML buttons through JavaScript using AJAX. Each time I click one of these buttons, it triggers an AJAX function. If the AJAX request is successful, it retrieves a number that will be utilized in a for loop. As the loop ...

What is the proper way to eliminate the top margin from a div that has the display table-cell property?

There are two div elements with display: table-cell property: <div> This is a table cell </div> <div> <h1>Some content</h1> <h1>Some content</h1> <h1>Some content</h1> <h1>Som ...

jquery plugin causing issues with bootstrap button functionality

Currently, I am utilizing the jQuery form plug-in to post my form in an Ajax way. The post function is functioning perfectly with the default button. However, it seems to encounter issues when I try to use a custom Bootstrap button as shown below. Could so ...

Angular JS modal form popup is a sleek and user-friendly feature that

When trying to display a modal form popup, I encountered an issue where it was not appearing. Upon inspecting the element, I received an error stating that $(...)modal is not a function. AuthService.createAdmin(data, function (response) { ...

Steps to store user input into an array and subsequently combine the stored input:

I am currently working on a form that consists of two text boxes: Task and Description. My goal is to be able to log the input from both boxes and save it via a submit button. For example: Task: do laundry Description: do a buttload of laundry (idk lol) I ...

jquery mobile listview extension not functioning as expected

My JQM menu is displayed as a listview, and I want it to be normal on every page except one where it should have 2 extra items. Despite searching online for solutions, nothing seems to work. Here are some of the things I've attempted: -location.reloa ...

Is there a reason why the integration of OnsenUI with Vue using vue-onsenui and v-ons-segment is not functioning

I am experiencing an issue with OnsenUI and VUE vue-onsenui v-ons-segment. Instead of displaying a button bar in a row as expected, I am seeing standard buttons. The problematic code resides in a customized Monaca CLI project utilizing the minimal VUE tem ...

Unpacking jQuery's fancybox collection

Can anyone guide me to where I can locate the unpacked version of Jquery.fancybox-x.x.x.pack? I attempted to fix the code formatting manually in order to comprehend it better, but I'm still finding it challenging... ...

Embed a PHP function within an HTML tag using the echo statement

I have a unique function in my controller that retrieves the User's name here: public function fetchUser(){ $user_id = $this->session->userdata('ID'); return $this->users_model->get_user_name($user_id); } My goal is ...

Vue calendar - The month display is condensed to only one row

Currently, I am incorporating bootstrap-datetimepicker for Vue (found at https://www.npmjs.com/package/vue-bootstrap-datetimepicker) via CDN. The date display looks good, however there is an issue with the month view size when selecting a month. This is t ...

apply a course to the designated element

Alright, I have this piece of code that deals with session and page requests. // Let's start our session session_start(); // Now let's check if there is a page request if (isset($_GET['page'])) { // If there is a requested page, we ...

When triggered by a click, the function gradually fades in and out. It superimposes one image on top of another, but unfortunately, the sizes

Due to different screen sizes, the image does not appear on top of another image exactly. It seems like a new function is needed. One that does not overlap with another image (by clicking the function for a few seconds), but rather replaces it for those fe ...

When clicking on an HTML link pointing to a cloud, it does not open in the same frame but instead opens in a new window

I am currently working on an HTML5 application that features a screen divided into two parts. The left part of the screen contains a few links, and upon clicking one of these links, the resulting content should open in the right side frame within the same ...