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

Create a TypeScript view component that encapsulates an HTMLElement for seamless integration with TweenMax

Looking to develop my own basic view component class that encompasses an HTMLElement or JQuery element, I want to be able to do something similar to this: var newComponent:CustomComponent = new CustomComponent($('#someDiv')); TweenMax.to(newCom ...

How to access a particular tab in Bootstrap 5 using an external link

Is there a way to direct users to a specific tab upon clicking a link on another page? Check out the example below: <div class="products-btn"> <a href="products.html#pills-profile">view all</a> </div> On Pro ...

Tips for expanding the contentedible HTML element downwards within its container

When a user inputs content, I need the paragraph to expand only downward while the div with the ID of description also grows in the same direction. The main parent div labeled documentation should remain fixed so it doesn't expand upwards. This scenar ...

Is there a way to automatically trigger an Anthem.NET button click event upon page load in ASP.NET?

My goal is to initiate the loading of an ASP.NET page followed by triggering a server-side event that will update some HTML on the client side. The event is associated with an Anthem.NET imagebutton control, so the most obvious approach would be to simply ...

Assistance needed to make a jQuery carousel automatically rotate infinitely. Having trouble making the carousel loop continuously instead of rewinding

Currently, I am in the process of creating an auto-rotating image carousel using jQuery. My goal is to make the images rotate infinitely instead of rewinding back to the first image once the last one is reached. As a beginner in the world of jQuery, I&apos ...

Is there a way to spin the picture but keep the container still?

Is there a way to animate the rotation of the gradient color without rotating the rhombus? I attempted using transform: rotate, but it ended up rotating the entire shape. Any suggestions on how to achieve this effect? .box { position: absolute; top ...

I have combed through numerous discussions regarding the use of jQuery's :visible selector, specifically interested in any instances where conflicts arise when a <label for=""> element is present

Utilizing jquery to toggle the display of additional information when a user selects a radio option. Essentially, when the user clicks on the < dt > tag (either the < input > or < label >), the < dd > section slides and remains open to reveal more details. ...

phperror message is included and echoed

I am facing an issue while trying to utilize the php include tag for my header and footer sections. Even though my index file and header.php file seem to be error-free, the included file is not showing up when I preview it in a browser. Is there an easy fi ...

Creating a standalone static page in Wordpress showcasing the header, sidebar, and footer of the twenty fourteen template

I have created a static blank page containing my website's header, sidebar, and footer. However, I am trying to remove the 'style' that is being enforced by the CSS of my WordPress template on the page. Below is the code I am using: <?p ...

Having trouble with table sorting in Jquery?

I am a beginner in the realm of Jquery and web programming. Recently, I attempted to implement the tablesorter jquery plugin for one of my projects but encountered issues with making it work properly. In search of a solution, I turned to Stack Overflow. C ...

How can I add text to an HTML5 SVG similar to using the HTML5 <p> tag?

I am currently working on creating dynamic rectangular boxes and I am facing some difficulties with inserting text into the shapes. The SVG text requires setting x and y coordinates in separate text tags, and doesn't have built-in width and height pro ...

Troubleshooting Issue with jQuery replaceWith in Loop

Trying to make it so that when the update button is clicked, the text on the left side becomes an input box: Click the update button and the text on the left will be an input box However, instead of just one input box appearing, all the text on the left s ...

When trying to load a php page2 into page1 via ajax, the Javascript code fails to execute

Currently, I am in the process of learning PHP and JavaScript. I have encountered a particular issue with a webpage setup. Let's say I have a page called page1 which consists of two input fields and a button labeled 'Go'. Upon clicking the & ...

Include a basic downward-pointing arrow graphic to enhance the drop-down navigation menus

I am working on a website that has drop-down menu headings styled with CSS. I am looking to enhance these certain menu headers by adding small down-facing arrows indicating they are clickable. Does anyone have any suggestions on how I can achieve this? ...

Utilizing the data sent to a modal to enhance the functionality of the code by combining it with the src attribute of an <embed> element

I am trying to pass a dynamic string of data to a Bootstrap modal using the data-val attribute in an HTML table. <td <button type="button" class="btn btn-success btn-xs" data-val="<?php echo $row['order_id']; ?&g ...

Utilizing PHP and jQuery Ajax in conjunction with infinite scroll functionality to enhance filtering capabilities

I have implemented infinite-ajax-scroll in my PHP Laravel project. This project displays a long list of divs and instead of using pagination, I opted to show all results on the same page by allowing users to scroll down. The filtering functionality works s ...

Harnessing the power of flexbox for data visualization

Trying to use flexbox to display data from a dataset in my code. Here is the code snippet: <div ng-app="myapp" ng-controller="FirstCtrl"> <div ng-repeat="name in names" class="graph-wrapper"> <div class="aside-1 content"> ...

Converting HTML to PDF using AngularJS

Can anyone help me with converting HTML to PDF in Angular? I have tried using the angular-save-html-to-pdf package from npm, but encountered errors. Are there any other solutions or custom directives available for this task? ...

Ways to control the number of boxes that are checked

I am currently working on a script to restrict the number of checkboxes that can be checked, but I am encountering an issue where the script is disabling all checkboxes on the page. Is there a way to only disable a specific checkbox within a certain div? ...

Want to analyze the response time of jQuery AJAX and initiate specific actions based on the results

$.ajax({ url: "http://ajaxhttpheaders.appspot.com", dataType: 'jsonp', success: function(headers) { language = headers['Accept-Language']; alert(language); }, //it& ...