Class

Position (engine/model)

@ckeditor/ckeditor5-engine/src/model/position

class

Represents a position in the model tree.

A position is represented by its root and a path in that root.

You can create position instances via its constructor or the createPosition*() factory methods of Model and Writer.

Note: Position is based on offsets, not indexes. This means that a position between two text nodes foo and bar has offset 3, not 1. See path for more information.

Since a position in the model is represented by a position root and position path it is possible to create positions placed in non-existing places. This requirement is important for operational transformation algorithms.

Also, operations kept in the document history are storing positions (and ranges) which were correct when those operations were applied, but may not be correct after the document has changed.

When changes are applied to the model, it may also happen that position parent will change even if position path has not changed. Keep in mind, that if a position leads to non-existing element, parent and some other properties and methods will throw errors.

In most cases, position with wrong path is caused by an error in code, but it is sometimes needed, as described above.

Filtering

Properties

  • readonly

    index : number

    Position offset converted to an index in position's parent node. It is equal to the index of a node after this position. If position is placed in text node, position index is equal to the index of that text node.

  • readonly

    isAtEnd : boolean

    Is true if position is at the end of its parent, false otherwise.

  • readonly

    isAtStart : boolean

    Is true if position is at the beginning of its parent, false otherwise.

  • readonly

    nodeAfter : null | Node

    Node directly after this position. Returns null if this position is at the end of its parent, or if it is in a text node.

  • readonly

    nodeBefore : null | Node

    Node directly before this position. Returns null if this position is at the start of its parent, or if it is in a text node.

  • offset : number

    Offset at which this position is located in its parent. It is equal to the last item in position path.

    Parameters

    newOffset : number
  • readonly

    parent : Element | DocumentFragment

    Parent element of this position.

    Keep in mind that parent value is calculated when the property is accessed. If position path leads to a non-existing element, parent property will throw error.

    Also it is a good idea to cache parent property if it is used frequently in an algorithm (i.e. in a long loop).

  • readonly

    path : readonly Array<number>

    Position of the node in the tree. Path contains offsets, not indexes.

    Position can be placed before, after or in a node if that node has offsetSize greater than 1. Items in position path are starting offsets of position ancestors, starting from direct root children, down to the position offset in it's parent.

    ROOT
     |- P            before: [ 0 ]         after: [ 1 ]
     |- UL           before: [ 1 ]         after: [ 2 ]
        |- LI        before: [ 1, 0 ]      after: [ 1, 1 ]
        |  |- foo    before: [ 1, 0, 0 ]   after: [ 1, 0, 3 ]
        |- LI        before: [ 1, 1 ]      after: [ 1, 2 ]
           |- bar    before: [ 1, 1, 0 ]   after: [ 1, 1, 3 ]
    

    foo and bar are representing text nodes. Since text nodes has offset size greater than 1 you can place position offset between their start and end:

    ROOT
     |- P
     |- UL
        |- LI
        |  |- f^o|o  ^ has path: [ 1, 0, 1 ]   | has path: [ 1, 0, 2 ]
        |- LI
           |- b^a|r  ^ has path: [ 1, 1, 1 ]   | has path: [ 1, 1, 2 ]
    
  • readonly

    root : Element | DocumentFragment

    Root of the position path.

  • stickiness : PositionStickiness

    Position stickiness. See PositionStickiness.

  • readonly

    textNode : null | Text

    Returns text node instance in which this position is placed or null if this position is not in a text node.

Methods

  • constructor( root, path, stickiness )

    Creates a position.

    Parameters

    root : Element | DocumentFragment

    Root of the position.

    path : readonly Array<number>

    Position path. See path.

    stickiness : PositionStickiness

    Position stickiness. See PositionStickiness.

    Defaults to 'toNone'

  • clone() → Position

    Returns a new position that is equal to current position.

    Returns

    Position
  • compareWith( otherPosition ) → PositionRelation

    Checks whether this position is before or after given position.

    This method is safe to use it on non-existing positions (for example during operational transformation).

    Parameters

    otherPosition : Position

    Returns

    PositionRelation
  • findAncestor( parentName ) → null | Element

    Returns the parent element of the given name. Returns null if the position is not inside the desired parent.

    Parameters

    parentName : string

    The name of the parent element to find.

    Returns

    null | Element
  • getAncestors() → Array<Element | DocumentFragment>

    Returns ancestors array of this position, that is this position's parent and its ancestors.

    Returns

    Array<Element | DocumentFragment>

    Array with ancestors.

  • getCommonAncestor( position ) → null | Element | DocumentFragment

    Returns an Element or DocumentFragment which is a common ancestor of both positions. The roots of these two positions must be identical.

    Parameters

    position : Position

    The second position.

    Returns

    null | Element | DocumentFragment
  • getCommonPath( position ) → Array<number>

    Returns the slice of two position paths which is identical. The roots of these two paths must be identical.

    This method is safe to use it on non-existing positions (for example during operational transformation).

    Parameters

    position : Position

    The second position.

    Returns

    Array<number>

    The common path.

  • getLastMatchingPosition( skip, options ) → Position

    Gets the farthest position which matches the callback using TreeWalker.

    For example:

    getLastMatchingPosition( value => value.type == 'text' );
    // <paragraph>[]foo</paragraph> -> <paragraph>foo[]</paragraph>
    
    getLastMatchingPosition( value => value.type == 'text', { direction: 'backward' } );
    // <paragraph>foo[]</paragraph> -> <paragraph>[]foo</paragraph>
    
    getLastMatchingPosition( value => false );
    // Do not move the position.
    

    Parameters

    skip : ( TreeWalkerValue ) => boolean

    Callback function. Gets TreeWalkerValue and should return true if the value should be skipped or false if not.

    options : TreeWalkerOptions

    Object with configuration options. See TreeWalker.

    Defaults to {}

    Returns

    Position

    The position after the last item which matches the skip callback test.

  • getParentPath() → Array<number>

    Returns a path to this position's parent. Parent path is equal to position path but without the last item.

    This method is safe to use it on non-existing positions (for example during operational transformation).

    Returns

    Array<number>

    Path to the parent.

  • getShiftedBy( shift ) → Position

    Returns a new instance of Position, that has same parent but it's offset is shifted by shift value (can be a negative value).

    This method is safe to use it on non-existing positions (for example during operational transformation).

    Parameters

    shift : number

    Offset shift. Can be a negative value.

    Returns

    Position

    Shifted position.

  • getTransformedByOperation( operation ) → Position

    Returns a copy of this position that is transformed by given operation.

    The new position's parameters are updated accordingly to the effect of the operation.

    For example, if n nodes are inserted before the position, the returned position offset will be increased by n. If the position was in a merged element, it will be accordingly moved to the new element, etc.

    This method is safe to use it on non-existing positions (for example during operational transformation).

    Parameters

    operation : Operation

    Operation to transform by.

    Returns

    Position

    Transformed position.

  • hasSameParentAs( position ) → boolean

    Checks if two positions are in the same parent.

    This method is safe to use it on non-existing positions (for example during operational transformation).

    Parameters

    position : Position

    Position to compare with.

    Returns

    boolean

    true if positions have the same parent, false otherwise.

  • inherited

    is( type ) → this is Element | RootElement

    Checks whether the object is of type Element or its subclass.

    element.is( 'element' ); // -> true
    element.is( 'node' ); // -> true
    element.is( 'model:element' ); // -> true
    element.is( 'model:node' ); // -> true
    
    element.is( 'view:element' ); // -> false
    element.is( 'documentSelection' ); // -> false
    

    Assuming that the object being checked is an element, you can also check its name:

    element.is( 'element', 'imageBlock' ); // -> true if this is an <imageBlock> element
    text.is( 'element', 'imageBlock' ); -> false
    

    Parameters

    type : 'element' | 'model:element'

    Returns

    this is Element | RootElement
  • inherited

    is( type ) → this is RootElement

    Checks whether the object is of type RootElement.

    rootElement.is( 'rootElement' ); // -> true
    rootElement.is( 'element' ); // -> true
    rootElement.is( 'node' ); // -> true
    rootElement.is( 'model:rootElement' ); // -> true
    rootElement.is( 'model:element' ); // -> true
    rootElement.is( 'model:node' ); // -> true
    
    rootElement.is( 'view:element' ); // -> false
    rootElement.is( 'documentFragment' ); // -> false
    

    Assuming that the object being checked is an element, you can also check its name:

    rootElement.is( 'rootElement', '$root' ); // -> same as above
    

    Parameters

    type : 'rootElement' | 'model:rootElement'

    Returns

    this is RootElement
  • inherited

    is( type ) → this is Position | LivePosition

    Checks whether the object is of type Position or its subclass.

    position.is( 'position' ); // -> true
    position.is( 'model:position' ); // -> true
    
    position.is( 'view:position' ); // -> false
    position.is( 'documentSelection' ); // -> false
    

    Parameters

    type : 'position' | 'model:position'

    Returns

    this is Position | LivePosition
  • inherited

    is( type ) → this is Range | LiveRange

    Checks whether the object is of type Range or its subclass.

    range.is( 'range' ); // -> true
    range.is( 'model:range' ); // -> true
    
    range.is( 'view:range' ); // -> false
    range.is( 'documentSelection' ); // -> false
    

    Parameters

    type : 'range' | 'model:range'

    Returns

    this is Range | LiveRange
  • inherited

    is( type ) → this is LivePosition

    Checks whether the object is of type LivePosition.

    livePosition.is( 'position' ); // -> true
    livePosition.is( 'model:position' ); // -> true
    livePosition.is( 'liveposition' ); // -> true
    livePosition.is( 'model:livePosition' ); // -> true
    
    livePosition.is( 'view:position' ); // -> false
    livePosition.is( 'documentSelection' ); // -> false
    

    Parameters

    type : 'livePosition' | 'model:livePosition'

    Returns

    this is LivePosition
  • inherited

    is( type ) → this is Text

    Checks whether the object is of type Text.

    text.is( '$text' ); // -> true
    text.is( 'node' ); // -> true
    text.is( 'model:$text' ); // -> true
    text.is( 'model:node' ); // -> true
    
    text.is( 'view:$text' ); // -> false
    text.is( 'documentSelection' ); // -> false
    

    Note: Until version 20.0.0 this method wasn't accepting '$text' type. The legacy 'text' type is still accepted for backward compatibility.

    Parameters

    type : '$text' | 'model:$text'

    Returns

    this is Text
  • inherited

    is( type ) → this is DocumentFragment

    Checks whether the object is of type DocumentFragment.

    docFrag.is( 'documentFragment' ); // -> true
    docFrag.is( 'model:documentFragment' ); // -> true
    
    docFrag.is( 'view:documentFragment' ); // -> false
    docFrag.is( 'element' ); // -> false
    docFrag.is( 'node' ); // -> false
    

    Parameters

    type : 'documentFragment' | 'model:documentFragment'

    Returns

    this is DocumentFragment
  • inherited

    is( type ) → this is TextProxy

    Checks whether the object is of type TextProxy.

    textProxy.is( '$textProxy' ); // -> true
    textProxy.is( 'model:$textProxy' ); // -> true
    
    textProxy.is( 'view:$textProxy' ); // -> false
    textProxy.is( 'range' ); // -> false
    

    Note: Until version 20.0.0 this method wasn't accepting '$textProxy' type. The legacy 'textProxt' type is still accepted for backward compatibility.

    Parameters

    type : '$textProxy' | 'model:$textProxy'

    Returns

    this is TextProxy
  • inherited

    is( type, name ) → boolean

    Checks whether the object is of type RootElement and has the specified name.

    rootElement.is( 'rootElement', '$root' );
    

    Type parameters

    N : extends string

    Parameters

    type : 'rootElement' | 'model:rootElement'
    name : N

    Returns

    boolean
  • inherited

    is( type, name ) → boolean

    Checks whether the object is of type Element or its subclass and has the specified name.

    element.is( 'element', 'imageBlock' ); // -> true if this is an <imageBlock> element
    text.is( 'element', 'imageBlock' ); -> false
    

    Type parameters

    N : extends string

    Parameters

    type : 'element' | 'model:element'
    name : N

    Returns

    boolean
  • inherited

    is( type ) → this is Marker

    Checks whether the object is of type Marker.

    marker.is( 'marker' ); // -> true
    marker.is( 'model:marker' ); // -> true
    
    marker.is( 'view:element' ); // -> false
    marker.is( 'documentSelection' ); // -> false
    

    Parameters

    type : 'marker' | 'model:marker'

    Returns

    this is Marker
  • inherited

    is( type ) → this is DocumentSelection

    Checks whether the object is of type DocumentSelection.

    selection.is( 'selection' ); // -> true
    selection.is( 'documentSelection' ); // -> true
    selection.is( 'model:selection' ); // -> true
    selection.is( 'model:documentSelection' ); // -> true
    
    selection.is( 'view:selection' ); // -> false
    selection.is( 'element' ); // -> false
    selection.is( 'node' ); // -> false
    

    Parameters

    type : 'documentSelection' | 'model:documentSelection'

    Returns

    this is DocumentSelection
  • inherited

    is( type ) → this is Selection | DocumentSelection

    Checks whether the object is of type Selection or DocumentSelection.

    selection.is( 'selection' ); // -> true
    selection.is( 'model:selection' ); // -> true
    
    selection.is( 'view:selection' ); // -> false
    selection.is( 'range' ); // -> false
    

    Parameters

    type : 'selection' | 'model:selection'

    Returns

    this is Selection | DocumentSelection
  • inherited

    is( type ) → this is LiveRange

    Checks whether the object is of type LiveRange.

    liveRange.is( 'range' ); // -> true
    liveRange.is( 'model:range' ); // -> true
    liveRange.is( 'liveRange' ); // -> true
    liveRange.is( 'model:liveRange' ); // -> true
    
    liveRange.is( 'view:range' ); // -> false
    liveRange.is( 'documentSelection' ); // -> false
    

    Parameters

    type : 'liveRange' | 'model:liveRange'

    Returns

    this is LiveRange
  • inherited

    is( type ) → this is Node | Text | Element | RootElement

    Checks whether the object is of type Node or its subclass.

    This method is useful when processing model objects that are of unknown type. For example, a function may return a DocumentFragment or a Node that can be either a text node or an element. This method can be used to check what kind of object is returned.

    someObject.is( 'element' ); // -> true if this is an element
    someObject.is( 'node' ); // -> true if this is a node (a text node or an element)
    someObject.is( 'documentFragment' ); // -> true if this is a document fragment
    

    Since this method is also available on a range of view objects, you can prefix the type of the object with model: or view: to check, for example, if this is the model's or view's element:

    modelElement.is( 'model:element' ); // -> true
    modelElement.is( 'view:element' ); // -> false
    

    By using this method it is also possible to check a name of an element:

    imageElement.is( 'element', 'imageBlock' ); // -> true
    imageElement.is( 'element', 'imageBlock' ); // -> same as above
    imageElement.is( 'model:element', 'imageBlock' ); // -> same as above, but more precise
    

    Parameters

    type : 'node' | 'model:node'

    Returns

    this is Node | Text | Element | RootElement
  • isAfter( otherPosition ) → boolean

    Checks whether this position is after given position.

    This method is safe to use it on non-existing positions (for example during operational transformation).

    Parameters

    otherPosition : Position

    Position to compare with.

    Returns

    boolean

    True if this position is after given position.

    Related:

  • isBefore( otherPosition ) → boolean

    Checks whether this position is before given position.

    Note: watch out when using negation of the value returned by this method, because the negation will also be true if positions are in different roots and you might not expect this. You should probably use a.isAfter( b ) || a.isEqual( b ) or !a.isBefore( p ) && a.root == b.root in most scenarios. If your condition uses multiple isAfter and isBefore checks, build them so they do not use negated values, i.e.:

    if ( a.isBefore( b ) && c.isAfter( d ) ) {
    	// do A.
    } else {
    	// do B.
    }
    

    or, if you have only one if-branch:

    if ( !( a.isBefore( b ) && c.isAfter( d ) ) {
    	// do B.
    }
    

    rather than:

    if ( !a.isBefore( b ) || && !c.isAfter( d ) ) {
    	// do B.
    } else {
    	// do A.
    }
    

    This method is safe to use it on non-existing positions (for example during operational transformation).

    Parameters

    otherPosition : Position

    Position to compare with.

    Returns

    boolean

    True if this position is before given position.

  • isEqual( otherPosition ) → boolean

    Checks whether this position is equal to given position.

    This method is safe to use it on non-existing positions (for example during operational transformation).

    Parameters

    otherPosition : Position

    Position to compare with.

    Returns

    boolean

    True if positions are same.

  • isTouching( otherPosition ) → boolean

    Checks whether this position is touching given position. Positions touch when there are no text nodes or empty nodes in a range between them. Technically, those positions are not equal but in many cases they are very similar or even indistinguishable.

    Parameters

    otherPosition : Position

    Position to compare with.

    Returns

    boolean

    True if positions touch.

  • isValid() → boolean

    Checks whether the position is valid in current model tree, that is whether it points to an existing place in the model.

    Returns

    boolean
  • toJSON() → unknown

    Returns

    unknown
  • internal

    _getCombined( source, target ) → Position

    Returns a new position that is a combination of this position and given positions.

    The combined position is a copy of this position transformed by moving a range starting at source position to the target position. It is expected that this position is inside the moved range.

    Example:

    let original = model.createPositionFromPath( root, [ 2, 3, 1 ] );
    let source = model.createPositionFromPath( root, [ 2, 2 ] );
    let target = model.createPositionFromPath( otherRoot, [ 1, 1, 3 ] );
    original._getCombined( source, target ); // path is [ 1, 1, 4, 1 ], root is `otherRoot`
    

    Explanation:

    We have a position [ 2, 3, 1 ] and move some nodes from [ 2, 2 ] to [ 1, 1, 3 ]. The original position was inside moved nodes and now should point to the new place. The moved nodes will be after positions [ 1, 1, 3 ], [ 1, 1, 4 ], [ 1, 1, 5 ]. Since our position was in the second moved node, the transformed position will be in a sub-tree of a node at [ 1, 1, 4 ]. Looking at original path, we took care of [ 2, 3 ] part of it. Now we have to add the rest of the original path to the transformed path. Finally, the transformed position will point to [ 1, 1, 4, 1 ].

    Parameters

    source : Position

    Beginning of the moved range.

    target : Position

    Position where the range is moved.

    Returns

    Position

    Combined position.

  • internal

    _getTransformedByDeletion( deletePosition, howMany ) → null | Position

    Returns a copy of this position that is updated by removing howMany nodes starting from deletePosition. It may happen that this position is in a removed node. If that is the case, null is returned instead.

    Parameters

    deletePosition : Position

    Position before the first removed node.

    howMany : number

    How many nodes are removed.

    Returns

    null | Position

    Transformed position or null.

  • internal

    _getTransformedByInsertOperation( operation ) → Position

    Returns a copy of this position transformed by an insert operation.

    Parameters

    operation : InsertOperation

    Returns

    Position
  • internal

    _getTransformedByInsertion( insertPosition, howMany ) → Position

    Returns a copy of this position that is updated by inserting howMany nodes at insertPosition.

    Parameters

    insertPosition : Position

    Position where nodes are inserted.

    howMany : number

    How many nodes are inserted.

    Returns

    Position

    Transformed position.

  • internal

    _getTransformedByMergeOperation( operation ) → Position

    Returns a copy of this position transformed by merge operation.

    Parameters

    operation : MergeOperation

    Returns

    Position
  • internal

    _getTransformedByMove( sourcePosition, targetPosition, howMany ) → Position

    Returns a copy of this position that is updated by moving howMany nodes from sourcePosition to targetPosition.

    Parameters

    sourcePosition : Position

    Position before the first element to move.

    targetPosition : Position

    Position where moved elements will be inserted.

    howMany : number

    How many consecutive nodes to move, starting from sourcePosition.

    Returns

    Position

    Transformed position.

  • internal

    _getTransformedByMoveOperation( operation ) → Position

    Returns a copy of this position transformed by a move operation.

    Parameters

    operation : MoveOperation

    Returns

    Position
  • internal

    _getTransformedBySplitOperation( operation ) → Position

    Returns a copy of this position transformed by a split operation.

    Parameters

    operation : SplitOperation

    Returns

    Position

Static methods

  • static

    fromJSON( json, doc ) → Position

    Creates a Position instance from given plain object (i.e. parsed JSON string).

    Parameters

    json : any

    Plain object to be converted to Position.

    doc : Document

    Document object that will be position owner.

    Returns

    Position

    Position instance created using given plain object.

  • internal static

    _createAfter( item, [ stickiness ] ) → Position

    Creates a new position, after given model item.

    Parameters

    item : DocumentFragment | Item

    Item after which the position should be placed.

    [ stickiness ] : PositionStickiness

    Position stickiness.

    Returns

    Position
  • internal static

    _createAt( itemOrPosition, [ offset ], stickiness ) → Position

    Creates position at the given location. The location can be specified as:

    • a position,
    • parent element and offset (offset defaults to 0),
    • parent element and 'end' (sets position at the end of that element),
    • model item and 'before' or 'after' (sets position before or after given model item).

    This method is a shortcut to other factory methods such as:

    Parameters

    itemOrPosition : DocumentFragment | Position | Item
    [ offset ] : PositionOffset

    Offset or one of the flags. Used only when the first parameter is a model item.

    stickiness : PositionStickiness

    Position stickiness. Used only when the first parameter is a model item.

    Defaults to 'toNone'

    Returns

    Position
  • internal static

    _createBefore( item, [ stickiness ] ) → Position

    Creates a new position, before the given model item.

    Parameters

    item : DocumentFragment | Item

    Item before which the position should be placed.

    [ stickiness ] : PositionStickiness

    Position stickiness.

    Returns

    Position